vuejs/vue-router · info

<router-link> with to=

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.

Solutions

  1. Remove the tag prop and use the v-slot API: <router-link custom v-slot="{ href, navigate }"> and render your own element.
  2. For list items, wrap: <li><router-link to="...">...</router-link></li>, or use v-slot to render the <li> yourself with navigate on click.
  3. 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

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


AI-assisted analysis of vuejs/vue-router@680ccc68c5 (2026-09-02). Data as JSON: /api/errors/251f03a9469c636d. Report an issue: GitHub.

Appendix: 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)