Closed pi0 closed 2 years ago
Awesome!
From my understanding, Nuxt 3 already provides a <NuxtLink />
component that is just a renamed <RouterLink />
: https://github.com/nuxt/framework/blob/main/packages/nuxt3/src/pages/runtime/router.ts#L19
I don't have much knowledge about prefetching Nuxt payloads as I never had the opportunity to dive into Nuxt 2 <NuxtLink />
internals. However I do have some experience building "agnostic" link components, like the one pictured in my blog post, or more recently the one created for @prismicio/vue@next
targeting Vue 3: https://github.com/prismicio/prismic-vue/blob/v3/src/components/PrismicLink.ts#L133-L266
The latter might be of some help/inspiration while creating Nuxt 3 <NuxtLink />
component. In that regard, I'm happy to help, or even provide a first draft PR or spec that implements the "agnostic" part of such component that I'm familiar with!
@lihbr That would be great! That agnostic behaviour is exactly what we'd like to include in NuxtLink
👍
I second this. Indeed the goal is to make NuxtLink
an agnostic component that optionally supports Router navigations
Here's a first naive/draft specs for it, take it mainly as a bundle of ideas regarding the new <NuxtLink>
component. I guess we can work from here and add the prefetching part to it. (feel free to move it to a more convenient place also)
Labels are going as follows:
This document presents the specifications (specs) of a <NuxtLink>
component for Nuxt 3. It reimplements most Nuxt 2 <NuxtLink>
component features while also allowing the new component to also act as a drop-in replacement for HTML anchor (<a>
) tag, effectively making the new <NuxtLink>
component agnostic regarding the type of links (external or internal) it handles.
Nuxt 2 provides a link component for internal links that extends Vue Router's <RouterLink>
component. On top of that, it also handles smart prefetching of links as they become available in the viewport, assuming user's connection is fast enough for that feature (not offline nor 2g).
As of today, Nuxt 3 link component is just a reexport of Vue Router's <RouterLink>
component, with no extends of any sort on top of it.
The following describes how Nuxt 3 <NuxtLink>
component could work.
The <NuxtLink>
component accepts the following props (interface):
type NuxtLinkProps = {
// Routing
to?: string;
href?: string;
// Attributes
blank?: boolean;
target?: string;
rel?: string;
// Prefetching
prefetch?: boolean;
noPrefetch?: boolean;
// Styling
activeClass?: string;
exactActiveClass?: string;
prefetchedClass?: string;
// Vue Router's `<RouterLink>` additional props
replace?: boolean;
ariaCurrentValue?: string;
// Edge cases handling
external?: boolean;
internal?: boolean;
};
The following props can be configured globally inside Nuxt config router
object. They will be used with a lower priority than direct props by <NuxtLink>
components:
import { defineNuxtConfig } from "nuxt3";
export default defineNuxtConfig({
router: {
prefetchLinks: false, // defaults to `true`
linkExternalRelAttribute: "noopener", // defaults to `"noopener noreferrer"`
linkActiveClass: "foo", // defaults to `nuxt-link-active`
linkExactActiveClass: "bar", // defaults to `nuxt-link-exact-active`
linkPrefetchedClass: "baz", // defaults to `nuxt-link-prefetched`
},
});
to
(string
)⚪ Status: to be discussed
The to
prop accepts any kind of URLs.
/pages
is active) and the URL is detected as internal (whatever approach we find bullet-proof-enough for it, to be discussed), then the <RouterLink>
component gets rendered;<a>
tag is rendered with a rel
attribute equal to noopener noreferer
(value of router.linkExternalRelAttribute
).<NuxtLink to="/foo">...</NuxtLink>
<!-- Renders as: -->
<RouterLink to="/foo">...</RouterLink>
<NuxtLink to="https://example.com">...</NuxtLink>
<!-- Renders as: -->
<a href="https://example.com" rel="noopener noreferrer">...</a>
Errors & warnings
to
prop is not provided, so is the href
prop, an error is thrown by the component;to
prop is used in conjunction with the href
prop, a warning is emitted and the to
prop takes the priority over the href
prop (however could go the other way around, depending on how we want to present the component).href
(string
)⚪ Status: to be discussed
The href
prop is an alias to the to
prop.
to
prop;<NuxtLink>
component to be presented as a "drop-in replacement for the native <a>
tag", in the same fashion as @nuxt/image
<NuxtImg>
and <NuxtPicture>
components.<NuxtLink href="/foo">...</NuxtLink>
<!-- Renders as: -->
<RouterLink to="/foo">...</RouterLink>
<NuxtLink href="https://example.com">...</NuxtLink>
<!-- Renders as: -->
<a href="https://example.com" rel="noopener noreferrer">...</a>
Errors & warnings
href
prop is used in conjunction with the to
prop, a warning is emitted and the to
prop takes the priority over the href
prop (however could go the other way around, depending on how we want to present the component);href
prop is not provided, so is the to
prop, an error is thrown by the component.blank
(boolean
)⚪ Status: to be discussed
The blank
prop is a sugar for making any kind of link open in a new tab.
blank
is used, the link is treated as an external link and rendered with an <a>
tag;target
and rel
attributes are set respectively to _blank
and noopener noreferer
(value of router.linkExternalRelAttribute
).<NuxtLink to="/foo" blank>...</NuxtLink>
<!-- Renders as: -->
<a href="/foo" target="_blank" rel="noopener noreferrer">...</a>
<NuxtLink to="https://example.com">...</NuxtLink>
<!-- Renders as: -->
<a href="https://example.com" target="_blank" rel="noopener noreferrer">...</a>
target
(string
)⚪ Status: to be discussed
The target
prop allows users to set the target
attribute of the rendered link.
target
is used, an <a>
tag is systematically rendered regardless if the link represents an internal link;target
prop takes priority over the blank
prop target
attribute value when used together.<NuxtLink to="/foo" target="_blank">...</NuxtLink>
<!-- Renders as: -->
<a href="/foo" target="_blank" rel="noopener noreferrer">...</a>
<NuxtLink to="https://example.com" target="_blank">...</NuxtLink>
<!-- Renders as: -->
<a href="https://example.com" target="_blank" rel="noopener noreferrer">...</a>
<NuxtLink to="https://example.com" blank target="bar">...</NuxtLink>
<!-- Renders as: -->
<a href="https://example.com" target="bar" rel="noopener noreferrer">...</a>
Setting target
prop to _blank
performs effectively the same action as using the blank
prop alone.
rel
(string
)⚪ Status: to be discussed
The rel
prop allows users to set the rel
attribute of the rendered link.
rel
prop takes priority over the rel
that could have been computed by the component.<NuxtLink to="/foo" rel="noopener">...</NuxtLink>
<!-- Renders as: -->
<a href="/foo" rel="noopener">...</a>
<NuxtLink to="https://example.com" rel="noopener">...</NuxtLink>
<!-- Renders as: -->
<a href="https://example.com" rel="noopener">...</a>
<NuxtLink to="https://example.com" blank rel="bar">...</NuxtLink>
<!-- Renders as: -->
<a href="https://example.com" target="_blank" rel="bar">...</a>
prefetch
(boolean
)⚪ Status: to be discussed
The prefetch
prop explicitely enables link prefetching.
prefetch
is used, it takes priority over the value set in Nuxt config router.prefetchLinks
;prefetch
is used with an external link, it is simply ignored.<!-- Will prefetch -->
<NuxtLink to="/foo" prefetch>...</NuxtLink>
<!-- No effect -->
<NuxtLink to="https://example.com">...</NuxtLink>
Errors & warnings
prefetch
prop is used in conjunction with the no-prefetch
prop, a warning is emitted and the prefetch
prop takes the priority over the no-prefetch
prop (however could go the other way around, depending on how we want to present the component, or we could just throw an error).no-prefetch
(boolean
)⚪ Status: to be discussed
The no-prefetch
prop explicitely disables link prefetching.
no-prefetch
is used, it takes priority over the value set in Nuxt config router.prefetchLinks
;no-prefetch
is used with an external link, it is simply ignored.<!-- Will not prefetch -->
<NuxtLink to="/foo" no-prefetch>...</NuxtLink>
<!-- No effect -->
<NuxtLink to="https://example.com">...</NuxtLink>
Errors & warnings
no-prefetch
prop is used in conjunction with the prefetch
prop, a warning is emitted and the prefetch
prop takes the priority over the no-prefetch
prop (however could go the other way around, depending on how we want to present the component, or we could just throw an error).active-class
(string
)⚪ Status: to be discussed
The active-class
prop is simply forwarded to the <RouterLink>
component when one is rendered.
active-class
prop is provided, the one from the Nuxt config is used;active-class
is used with an external link, it is simply ignored.<NuxtLink to="/foo" active-class="baz">...</NuxtLink>
<!-- Renders as on `/foo/bar`: -->
<RouterLink to="/foo" class="baz">...</RouterLink>
<NuxtLink to="https://example.com" active-class="baz">...</NuxtLink>
<!-- Renders as: -->
<a href="https://example.com" rel="noopener noreferrer">...</a>
See Vue Router's
<RouterLink>
props for more details.
exact-active-class
(string
)⚪ Status: to be discussed
The exact-active-class
prop is simply forwarded to the <RouterLink>
component when one is rendered.
exact-active-class
prop is provided, the one from the Nuxt config is used;exact-active-class
is used with an external link, it is simply ignored.<NuxtLink to="/foo/bar" exact-active-class="baz">...</NuxtLink>
<!-- Renders as on `/foo/bar`: -->
<RouterLink to="/foo" class="nuxt-link-active baz">...</RouterLink>
<NuxtLink to="https://example.com" exact-active-class="baz">...</NuxtLink>
<!-- Renders as: -->
<a href="https://example.com" rel="noopener noreferrer">...</a>
See Vue Router's
<RouterLink>
props for more details.
prefetched-class
(string
)⚪ Status: to be discussed
The prefetched-class
prop is appended to rendered classes once the internal link has been prefetched.
prefetched-class
prop is provided, the one from the Nuxt config is used;prefetched-class
is used with an external link, or when prefetching is disabled, it is simply ignored.<NuxtLink to="/foo" prefetched-class="bar">...</NuxtLink>
<!-- Renders as, once prefetched: -->
<RouterLink to="/foo" class="bar">...</RouterLink>
<NuxtLink to="https://example.com" prefetched-class="bar">...</NuxtLink>
<!-- Renders as: -->
<a href="https://example.com" rel="noopener noreferrer">...</a>
<RouterLink>
additional propsreplace
(boolean
)⚪ Status: to be discussed
The replace
prop is simply forwarded to the <RouterLink>
component when one is rendered.
replace
is used with an external link, it is simply ignored.See Vue Router's
<RouterLink>
props for more details.
aria-current-value
(string
)⚪ Status: to be discussed
The aria-current-value
prop is simply forwarded to the <RouterLink>
component when one is rendered.
aria-current-value
is used with an external link, it is simply ignored.See Vue Router's
<RouterLink>
props for more details.
external
(boolean
)⚪ Status: to be discussed
The external
prop forces the link to be rendered as an external link.
<NuxtLink to="/foo" external>...</NuxtLink>
<!-- Renders as: -->
<a href="/foo" rel="noopener noreferrer">...</a>
<a to="https://example.com" external>...</a>
<!-- Renders as: -->
<a href="https://example.com" rel="noopener noreferrer">...</a>
Errors & warnings
external
prop is used in conjunction with the internal
prop, a warning is emitted and the external
prop takes the priority over the internal
prop (however could go the other way around, depending on how we want to present the component, or we could just throw an error).internal
prop (boolean
)⚪ Status: to be discussed
The internal
prop forces the link to be rendered as an internal link.
<NuxtLink to="/foo" internal>...</NuxtLink>
<!-- Renders as: -->
<NuxtLink to="/foo">...</NuxtLink>
<a to="https://example.com" internal>...</a>
<!-- Renders as: -->
<NuxtLink to="https://example.com">...</NuxtLink>
Errors & warnings
internal
prop is used in conjunction with the external
prop, a warning is emitted and the external
prop takes the priority over the internal
prop (however could go the other way around, depending on how we want to present the component, or we could just throw an error).
Nuxt 2 has a build-in
NuxtLink
that is basically a wrapper over<RouterLink>
with some improvements like page smart prefetching. We shall support t for Nuxt 3 as a built-in app component:RouterLink
whenpages/
directory enabledRouterLink
Some more ideas to discover by @lihbr: https://lihbr.com/blog/one-link-component-to-rule-them-all