Encodera

Thanks for reading!

Donate to the site — we work hard for you.

Contents

Nested routes

Nested routes are needed when a group of pages shares a part: a blog admin section with its own menu, inside which "All posts" and "New post" change. The parent route renders the layout, and the child routes render the content inside it.

The section layout

Create src/views/AdminLayout.vue. It has its own RouterView — child pages go there:

<!-- src/views/AdminLayout.vue -->
<template>
  <section class="admin">
    <h1>Blog admin</h1>
    <nav>
      <RouterLink :to="{ name: 'admin-posts' }">All posts</RouterLink> |
      <RouterLink :to="{ name: 'admin-new-post' }">New post</RouterLink>
    </nav>
    <RouterView />
  </section>
</template>

And two simple pages:

<!-- src/views/AdminPostsView.vue -->
<template>
  <h2>All posts</h2>
  <p>A table of posts goes here.</p>
</template>
<!-- src/views/NewPostView.vue -->
<template>
  <h2>New post</h2>
  <p>A form goes here.</p>
</template>

Child routes

Child routes are listed in the children field. Their paths are written without a leading slash — they are appended to the parent's path:

// src/router/index.js
import { createRouter, createWebHistory } from 'vue-router'
import HomeView from '../views/HomeView.vue'
 
const router = createRouter({
  history: createWebHistory(import.meta.env.BASE_URL),
  routes: [
    { path: '/', name: 'home', component: HomeView },
    { path: '/about', name: 'about', component: () => import('../views/AboutView.vue') },
    { path: '/posts/:id', name: 'post', component: () => import('../views/PostView.vue') },
    {
      path: '/admin',
      component: () => import('../views/AdminLayout.vue'),
      children: [
        { path: '', name: 'admin-posts', component: () => import('../views/AdminPostsView.vue') },
        { path: 'new', name: 'admin-new-post', component: () => import('../views/NewPostView.vue') },
      ],
    },
    { path: '/:pathMatch(.*)*', name: 'not-found', component: () => import('../views/NotFoundView.vue') },
  ],
})
 
export default router

Open http://localhost:5173/admin — you see the heading "Blog admin", the menu and "All posts". At /admin/new the layout stays, and the content inside changes to "New post".

AddressWhat is rendered
/adminAdminLayout with AdminPostsView inside
/admin/newAdminLayout with NewPostView inside
/anythingNotFoundView

Lazy loading

component: () => import('...') loads a page's code only on the first visit to it. In the build each such page goes into a separate file, and the home page opens faster. The page everyone sees — the home page — is usually imported directly.

The 404 page

The last route with the path /:pathMatch(.*)* matches any address no other route matched. Create src/views/NotFoundView.vue for it:

<!-- src/views/NotFoundView.vue -->
<template>
  <h1>Page not found</h1>
  <RouterLink to="/">Home</RouterLink>
</template>

The address http://localhost:5173/abc shows "Page not found".

Common mistake

Forgetting RouterView in the parent layout. The address /admin/new opens without errors but shows only the heading and menu — the child page has nowhere to render. Every nesting level needs its own RouterView.

Thanks for reading!

Donate to the site — we work hard for you.