How to Ensure Your Vue Router Link Navigates to the Correct Component

To ensure your Vue Router link navigates correctly, you must register the router instance with app.use(router), define route records that map paths to components, and use the to prop with either a string path or location object.

When a user clicks a <router-link> in a Vue.js application, the framework must resolve that navigation intent to a specific component instance. According to the vuejs/core source code, this resolution depends on Vue’s internal resolveComponent helper and the global registration of router components. Understanding this mechanism ensures your vue router link navigates to the intended view every time.

Vue Router relies on Vue’s component resolution system to transform <router-link> and <router-view> tags into actual component instances. When you install Vue Router using app.use(router), the plugin globally registers RouterLink and RouterView components.

As implemented in packages/runtime-core/src/helpers/resolveAssets.ts (lines 22-27), Vue’s resolveComponent helper searches for components in the current instance’s local registrations first, then falls back to global registrations. This is why app.use(router) is critical—it ensures resolveComponent can find the router components when parsing your templates.

Step-by-Step: Configuring Vue Router for Correct Navigation

1. Create and Register the Router Instance

Before any vue router link navigates successfully, you must create a router instance and install it on the Vue application. This step globally registers the <router-link> and <router-view> components.

// main.ts
import { createApp } from 'vue'
import { createRouter, createWebHistory } from 'vue-router'
import App from './App.vue'

const router = createRouter({
  history: createWebHistory(),
  routes: [] // defined in next step
})

const app = createApp(App)
app.use(router) // Globally registers RouterLink and RouterView
app.mount('#app')

As shown in packages/runtime-core/src/componentPublicInstance.ts (lines 62-78), this installation process attaches the router instance to all component instances, enabling access to $router and $route properties.

2. Define Route Records with Component Mappings

Route records establish the connection between URLs and components. You can use eager loading (static imports) or lazy loading (dynamic imports) for your components.

// router configuration
const router = createRouter({
  history: createWebHistory(),
  routes: [
    {
      path: '/',
      name: 'home',
      component: () => import('./components/Home.vue') // Lazy-loaded
    },
    {
      path: '/about',
      name: 'about',
      component: () => import('./components/About.vue')
    },
    {
      path: '/profile/:id',
      name: 'profile',
      component: () => import('./components/Profile.vue')
    }
  ]
})

Vue normalizes component names using camelize and capitalize helpers during registration. Ensure your route record component names match the registered names exactly to avoid resolution failures.

The to prop accepts either a string path or a location object. Using named routes with location objects makes your links more maintainable.

<!-- App.vue -->
<template>
  <nav>
    <!-- String path -->
    <router-link to="/">Home</router-link>
    
    <!-- Named route with params -->
    <router-link :to="{ name: 'profile', params: { id: '123' } }">
      User Profile
    </router-link>
    
    <!-- Named route with query -->
    <router-link :to="{ name: 'about', query: { section: 'team' } }">
      About Us
    </router-link>
  </nav>

  <!-- Component outlet -->
  <router-view />
</template>

When clicked, the <router-link> component calls the router's push method with the resolved location, triggering the navigation flow.

4. Render Components with Router View

The <router-view> component acts as the outlet for matched components. Vue resolves this component through the same resolveComponent mechanism used for router-link.

As defined in packages/runtime-core/src/component.ts (lines 165-171), global component registrations are stored on the app context. When the route changes, <router-view> retrieves the matched component from the route record and renders it, swapping instances as the navigation history changes.

Understanding Component Resolution in Vue Core

Vue's component resolution system ensures that <router-link> and <router-view> are correctly instantiated. The resolveComponent helper in packages/runtime-core/src/helpers/resolveAssets.ts performs a hierarchical lookup:

  1. Local registration: Checks components option on the current instance
  2. Global registration: Checks appContext.components (populated by app.use(router))
  3. Warning: If not found, Vue warns about unresolved components

This resolution chain explains why installing the router before mounting the app is mandatory. Without app.use(router), the RouterLink and RouterView components remain unregistered, causing resolveComponent to fail when parsing templates containing these tags.

Troubleshooting Navigation Failures

If your vue router link navigates to the wrong component or fails to render:

  • Verify global registration: Ensure app.use(router) is called before app.mount(). Check that you're using the correct router instance created with createRouter.
  • Check route record matching: Confirm the path or name in your to prop exactly matches a defined route. Remember that params must be defined in the route path (:id) to work with named routes.
  • Validate component availability: For lazy-loaded routes, ensure the import path is correct. For eager loading, verify the component is imported and assigned to the route record.
  • Inspect component names: Vue normalizes component names during registration. If using string templates, ensure case sensitivity matches the registration (PascalCase vs kebab-case).

Summary

  • Global registration is mandatory: Call app.use(router) to register <router-link> and <router-view> globally, enabling Vue's resolveComponent helper to find them.
  • Route records map URLs to components: Define paths and components (eager or lazy-loaded) in the router configuration to establish navigation targets.
  • Use valid to prop values: Pass string paths or location objects with correct names and params to <router-link>.
  • Render with <router-view>: Place this outlet component where matched route components should display.
  • Resolution follows hierarchy: Vue checks local then global registrations via packages/runtime-core/src/helpers/resolveAssets.ts.

Frequently Asked Questions

This occurs when Vue cannot resolve the <router-link> component, usually because app.use(router) was not called before mounting the application. Without global registration, Vue's resolveComponent helper (located in packages/runtime-core/src/helpers/resolveAssets.ts) fails to find the RouterLink definition, treating the tag as an unknown custom element.

No. While you can manually import RouterLink from vue-router and register it locally in a component, standard Vue Router usage requires global registration via app.use(router). This installation process registers both RouterLink and RouterView on the application context, ensuring they are available to resolveComponent throughout the component tree.

What happens if the to prop points to a non-existent route?

If the to prop references a path or name not defined in your route records, Vue Router will still push the location to the history, but <router-view> will have no matched component to render. Depending on your router configuration, this may render nothing, show a fallback component (if you have a catch-all /* route), or trigger navigation guards that handle the 404 scenario.

How does Vue distinguish between local and global component registrations?

Vue checks local registrations first by looking at the components option on the current component instance. If not found, it falls back to appContext.components (global registrations) as implemented in packages/runtime-core/src/helpers/resolveAssets.ts. This hierarchical lookup means local components shadow global ones, but global registrations (like those created by app.use(router)) serve as the default for standard router components.

Have a question about this repo?

These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:

Share the following with your agent to get started:
curl -s "https://instagit.com/install.md"

Works with
Claude Codex Cursor VS Code OpenClaw Any MCP Client

Maintain an open-source project? Get it listed too →