diff --git a/examples/app-vitest-full/components/NuxtLinkWithIsActive.vue b/examples/app-vitest-full/components/NuxtLinkWithIsActive.vue
new file mode 100644
index 000000000..fdefb2937
--- /dev/null
+++ b/examples/app-vitest-full/components/NuxtLinkWithIsActive.vue
@@ -0,0 +1,44 @@
+
+
+
+
+
diff --git a/examples/app-vitest-full/pages/about.vue b/examples/app-vitest-full/pages/about.vue
new file mode 100644
index 000000000..47c7f157e
--- /dev/null
+++ b/examples/app-vitest-full/pages/about.vue
@@ -0,0 +1,3 @@
+
+ About page
+
diff --git a/examples/app-vitest-full/pages/about/team.vue b/examples/app-vitest-full/pages/about/team.vue
new file mode 100644
index 000000000..a56b5f632
--- /dev/null
+++ b/examples/app-vitest-full/pages/about/team.vue
@@ -0,0 +1,3 @@
+
+ About team page
+
diff --git a/examples/app-vitest-full/tests/nuxt/mount-suspended.spec.ts b/examples/app-vitest-full/tests/nuxt/mount-suspended.spec.ts
index e17f019a6..0308d1389 100644
--- a/examples/app-vitest-full/tests/nuxt/mount-suspended.spec.ts
+++ b/examples/app-vitest-full/tests/nuxt/mount-suspended.spec.ts
@@ -10,6 +10,7 @@ import App from '~/app.vue'
import OptionsComponent from '~/components/OptionsComponent.vue'
import WrapperTests from '~/components/WrapperTests.vue'
import LinkTests from '~/components/LinkTests.vue'
+import NuxtLinkWithIsActive from '~/components/NuxtLinkWithIsActive.vue'
import ExportDefaultComponent from '~/components/ExportDefaultComponent.vue'
import ExportDefineComponent from '~/components/ExportDefineComponent.vue'
@@ -542,6 +543,46 @@ it('renders links correctly', async () => {
expect(component.html()).toMatchInlineSnapshot(`"
"`)
})
+it('receives correct isActive and isExactActive values in custom slot', async () => {
+ const component = await mountSuspended(NuxtLinkWithIsActive, {
+ route: '/about',
+ })
+
+ const aboutLink = component.find('a[href="/about"]')
+ expect(aboutLink.classes()).toContain('active')
+ expect(aboutLink.attributes('data-is-active')).toBe('true')
+ expect(aboutLink.attributes('data-is-exact-active')).toBe('true')
+})
+
+it('does not apply isActive when route does not match', async () => {
+ const component = await mountSuspended(NuxtLinkWithIsActive, {
+ route: '/',
+ })
+
+ const aboutLink = component.find('a[href="/about"]')
+ expect(aboutLink.classes()).not.toContain('active')
+ expect(aboutLink.attributes('data-is-active')).toBe('false')
+ expect(aboutLink.attributes('data-is-exact-active')).toBe('false')
+})
+
+it.fails('keeps parent link active without exact match on nested routes', async () => {
+ // NuxtLink in mountSuspended currently falls back to exact path matching for nested routes. Keep this stronger assertion as a tracked regression.
+ const component = await mountSuspended(NuxtLinkWithIsActive, {
+ route: '/about/team',
+ })
+
+ const aboutLink = component.find('a[href="/about"]')
+ const teamLink = component.find('a[href="/about/team"]')
+
+ expect(aboutLink.classes()).toContain('active')
+ expect(aboutLink.attributes('data-is-active')).toBe('true')
+ expect(aboutLink.attributes('data-is-exact-active')).toBe('false')
+
+ expect(teamLink.classes()).toContain('active')
+ expect(teamLink.attributes('data-is-active')).toBe('true')
+ expect(teamLink.attributes('data-is-exact-active')).toBe('true')
+})
+
it('element should be changed', async () => {
const component = await mountSuspended(WrapperElement, { props: { as: 'div' } })
diff --git a/src/runtime-utils/components/RouterLink.ts b/src/runtime-utils/components/RouterLink.ts
index d1956640d..831156204 100644
--- a/src/runtime-utils/components/RouterLink.ts
+++ b/src/runtime-utils/components/RouterLink.ts
@@ -1,4 +1,5 @@
-import { defineComponent, h, useRouter } from '#imports'
+import { defineComponent, h } from '#imports'
+import { useLink } from 'vue-router'
export const RouterLink = defineComponent({
functional: true,
@@ -9,25 +10,28 @@ export const RouterLink = defineComponent({
},
custom: Boolean,
replace: Boolean,
- // Not implemented
activeClass: String,
exactActiveClass: String,
ariaCurrentValue: String,
},
setup: (props, { slots }) => {
- const navigate = () => {}
+ const link = useLink(props)
+
return () => {
- const route = useRouter().resolve(props.to)
+ const route = link.route.value
+ const href = link.href.value
+ const isActive = link.isActive.value
+ const isExactActive = link.isExactActive.value
return props.custom
- ? slots.default?.({ href: route.href, navigate, route })
+ ? slots.default?.({ href, navigate: link.navigate, route, isActive, isExactActive })
: h(
'a',
{
- href: route.href,
+ href,
onClick: (e: MouseEvent) => {
e.preventDefault()
- return navigate()
+ return link.navigate(e)
},
},
slots,