vuejs/vue-router · info
<router-link> with to="${this.to}" is trying to use a scoped
Error message
<router-link> with to="${this.to}" is trying to use a scoped slot but it didn't provide exactly one child. Wrapping the content with a span element. What it means
This is a dev-only deprecation warning: the tag prop of <router-link> is removed in Vue Router 4 and will have no effect going forward. The recommended replacement is the v-slot API, which lets you render any element yourself. The warning fires once (warnedTagProp flag) when tag is found in the component's propsData.
Source
Thrown at src/components/link.js:123
this.$scopedSlots.default &&
this.$scopedSlots.default({
href,
route,
navigate: handler,
isActive: classes[activeClass],
isExactActive: classes[exactActiveClass]
})
if (scopedSlot) {
if (process.env.NODE_ENV !== 'production' && !this.custom) {
!warnedCustomSlot && warn(false, 'In Vue Router 4, the v-slot API will by default wrap its content with an <a> element. Use the custom prop to remove this warning:\n<router-link v-slot="{ navigate, href }" custom></router-link>\n')
warnedCustomSlot = true
}
if (scopedSlot.length === 1) {
return scopedSlot[0]
} else if (scopedSlot.length > 1 || !scopedSlot.length) {
if (process.env.NODE_ENV !== 'production') {
warn(
false,
`<router-link> with to="${
this.to
}" is trying to use a scoped slot but it didn't provide exactly one child. Wrapping the content with a span element.`
)
}
return scopedSlot.length === 0 ? h() : h('span', {}, scopedSlot)
}
}
if (process.env.NODE_ENV !== 'production') {
if ('tag' in this.$options.propsData && !warnedTagProp) {
warn(
false,
`<router-link>'s tag prop is deprecated and has been removed in Vue Router 4. Use the v-slot API to remove this warning: https://next.router.vuejs.org/guide/migration/#removal-of-event-and-tag-props-in-router-link.`
)
warnedTagProp = true
}View on GitHub (pinned to 680ccc68c5)
Solutions
- Remove the tag prop and use the v-slot API: <router-link custom v-slot="{ href, navigate }"> and render your own element.
- For list items, wrap: <li><router-link to="...">...</router-link></li>, or use v-slot to render the <li> yourself with navigate on click.
- Update styling/markup to target the rendered <a> instead of the custom tag.
Example fix
// before
<router-link to="/about" tag="li">About</router-link>
// after
<router-link to="/about" custom v-slot="{ href, navigate }">
<li :href="href" @click="navigate">About</li>
</router-link> Defensive patterns
Strategy: validation
Prevention
- Give <router-link> v-slot content exactly one root element.
- Avoid comment nodes/fragments as slot roots; wrap multiple siblings in a single element.
- Use custom prop when you need full control over the rendered element.
- Test rendered DOM of custom links after refactors.
When it happens
Trigger: Passing tag="li" (or any tag) to <router-link> in a project running a vue-router 3.x beta/next or 4.x-compatible dev build.
Common situations: Upgrading from vue-router 3.x stable (where tag worked) to the 3.x next / 4.x migration path; following old tutorials that style router-links as list items via tag.
Related errors
- Failed to resolve async component ${key}: ${reason}
- <router-link>'s tag prop is deprecated and has been removed
- router.addRoutes() is deprecated and has been removed in Vue
- In Vue Router 4, the v-slot API will by default wrap its con
- [vue-router]: Missing current instance. ${method}() must be
AI-assisted analysis of vuejs/vue-router@680ccc68c5 (2026-09-02).
Data as JSON: /api/errors/251f03a9469c636d.
Report an issue: GitHub.