Skip to content

NuxtLink external / internal logic issues #25532

@harlan-zw

Description

@harlan-zw

Environment


  • Operating System: Linux
  • Node Version: v18.18.0
  • Nuxt Version: 3.10.0
  • CLI Version: 3.10.0
  • Nitro Version: 2.8.1
  • Package Manager: npm@10.2.3
  • Builder: -
  • User Config: app, devtools
  • Runtime Modules: -
  • Build Modules: -

Reproduction

https://stackblitz.com/edit/nuxt-starter-qntwss?file=package.json

Describe the bug

ℹ️ Still thinking through this issue and how to best articulate the higher level problems.

Several edge cases

Within the NuxtLink component, we have several bugs related to how "external" links are treated. Within the reproduction, you can see the following issues:

  1. Trailing slashes are applied to implicit external links
  2. Explicit external links do not when using vue-router object
  3. Explicit external links (that are relative) that use vue-router object add base and trailing slash
  4. Explicit external for relative path trailing slashes is applied
  5. Using navigate() with external link does not work

The majority of these are edge cases and have workarounds. The number of edge cases, however, does point us toward the insight that the logic within NuxtLink may need some attention.

The deeper issue

While working on adding support for app.trailingSlash config I was trying to understand how the NuxtLink component works. While digging through the code, it became clear there was some inconsistency in how we're handling logic in a couple of cases.

External Links

We have the external prop that will switch the rendering between a and RouterLink.

external: Forces the link to be considered as external (true) or internal (false). This is helpful to handle edge-cases

We also have a computed property called isExternal which toggles the same behavior (rendering an anchor tag instead of RouterLink), which is enabled with:

  1. Has been marked explicitly as external, meaning the end-user wants to bypass vue-router (probably?)
  2. Is implicitly external from being an absolute URL (has a protocol)
  3. Has a target attribute and it doesn't equal _self ( ⚠️ This seems like broken logic as vue-router handles this)
<template>
  <!-- behaves correctly -->
  <NuxtLink to="/foo" external> <!-- isExternal: true -->
  <NuxtLink to="https://example.com/foo"> <!-- isExternal: true -->
  <!-- things start getting weird -->
  <NuxtLink :to="{ path: 'https://google.com/foo' }" external> <!-- isExternal: false -->
  <NuxtLink to="/foo" target="_blank"> <!-- isExternal: true -->
</template>

For all external links we're currently adding rel="noopener noreferrer" which may not be the desired behavior, this seems like they should only apply to absolute links.

Trailing Slash Links

Trailing slashes config is always applied no matter what type of link it is. This means that:

  • creating a dedicated NuxtLink component to handle trailing slashes is required
  • using the default trailingSlash logic will always be broken when needing absolute links

Additional context

No response

Logs

No response

Metadata

Metadata

Assignees

Type

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions