NuxtLink aria-current on dynamic routes: which link should announce the current page?
0 reputation · 05 May 2022, 20:10 UTC
0 reputation · 05 May 2022, 20:10 UTC
I'm building the main navigation for a Nuxt 3 app and relying on <NuxtLink>'s automatic aria-current="page" handling so screen readers announce the active page. The nav mixes a static link (/), dynamic routes (/post/:id), and one external URL.
My uncertainty is how active matching behaves when the user moves between parameter values of the same dynamic route, e.g. from /post/123 to /post/456. Since both resolve to the same route record, it's unclear whether aria-current will track the actually-current link or get applied to the wrong one, and whether an external <NuxtLink> is simply never marked active.
I also can't find a documented way to customize the aria-current value (e.g. page vs location) or opt out per link without wrapping <NuxtLink> in a custom component. Assume Nuxt 3.x; I have not yet tested across screen readers.
Does <NuxtLink>'s active matching compare route params or only the route name/path pattern for dynamic routes? Is there a supported way to override or disable aria-current for individual links? What is the recommended pattern for keeping announcements unambiguous in a mixed static/dynamic/external nav?
For a mixed nav in Nuxt 3, the static link (/) and external link behave predictably, and the dynamic links are the only real risk area — but probably not for the reason you suspect. <NuxtLink> delegates its active state to Vue Router's RouterLink, which distinguishes between active (same route record) and exact active (same record and same location, including params). aria-current="page" is applied on the exact match, so moving from /post/123 to /post/456 should move the attribute to the correct link — params are part of the comparison.
That said, this behavior has shifted across Nuxt 3 point releases, and I have not verified it against your exact version. Treat the above as the expected behavior and confirm it with the check below before shipping.
<NuxtLink to="https://…"> are rendered as plain anchors outside the router's matching, so they never receive aria-current. That is correct behavior — an external link is never "the current page."<NuxtLink> to change the aria-current value (e.g. page vs location) or to opt out per link. Overriding requires your own wrapper./post/:id links should not both announce. If your nav only ever contains one link per dynamic pattern (the typical case — a nav links to a section, not to a specific post), the ambiguity you're worried about cannot occur at all.// pages or a test nav with two dynamic links
<NuxtLink to="/post/123">Post 123</NuxtLink>
<NuxtLink to="/post/456">Post 456</NuxtLink>Navigate between the two routes and inspect the rendered anchors in DevTools. Only the link whose to matches the current full path should carry aria-current="page". If both do, your Nuxt version's matching is pattern-only and you need the wrapper below.
Where the default is ambiguous — or where you want aria-current="location" for a sub-section — wrap the link and control the attribute yourself:
<script setup lang="ts">
const props = defineProps<{ to: string; currentValue?: string }>()
const route = useRoute()
const isCurrent = computed(() => route.fullPath === props.to)
</script>
<template>
<NuxtLink :to="to" :aria-current="isCurrent ? (currentValue ?? 'page') : undefined">
<slot />
</NuxtLink>
</template>Comparing route.fullPath to the target gives you exact, param-aware matching regardless of what the built-in behavior does in your version.
<NuxtLink> for static and section-level links — one link per route pattern, so matching is unambiguous.aria-current is correct there.One detail that would change this advice: your exact Nuxt version. If the DevTools check shows pattern-only matching, pin the wrapper approach everywhere rather than relying on defaults.
Use comments to ask for clarification. Post a solution as an answer.
No question comments on this page.