Introduction to Nuxt3 (Part 4) - Understanding Nuxt's Routing

| 8 min read
Author: noboru-kudo noboru-kudoの画像

This article has been automatically translated.
The original article is here.

In the previous session, we made it possible for the sample blog site application to fetch blog information via an API.

This time, we will look into routing used for page transitions in Nuxt applications. Similar to Nuxt2, in Nuxt development, there is no need to create routing definitions individually. Nuxt adopts file system-based routing, where the route mapping is determined by the structure under the pages directory.

Basics of Routing


As mentioned earlier, by default, Nuxt automatically generates route mappings based on the file structure. The pages directory of the demo blog site we created had the following structure:

├── index.vue
└── details.vue

With this arrangement, Nuxt creates the following mappings (path -> page component) as Vue Router definitions in the background:

  • / -> pages/index.vue
  • /details -> pages/details.vue

Of course, nested directory structures are also possible.

└── foo
    └── bar.vue

With this structure, /foo/bar maps to pages/foo/bar.vue. This is something familiar to those who have worked with Nuxt2.

Page Switching Programmatically


So far, we have been using [NuxtLink] for page transitions. This is a Vue component built into Nuxt. Of course, it is also possible to switch pages programmatically without using NuxtLink. In this case, use the navigateTo function built into Nuxt.

<script setup lang="ts">
const navigate = () => {
  return navigateTo({
    path: '/foo/bar',
    query: {
      baz: 'programmatic-navigation'

    <button @click="navigate">Page Transition Programmatically</button>

Like other Nuxt core APIs, navigateTo is subject to Auto Import, so it can be described without import. Note that when using navigateTo, you must use await or return the result of navigateTo as the return value of the function.

Catch-All Route


This is a route for when there is no matching mapping (Catch-all Route). In Nuxt3, you create a page component with the file name [...slug].vue. The part of slug can be any string.

The page component to be created is a normal Vue component like below.

  <p>{{ $route.params.slug }} Catch-all Route</p>

It's a simple component with only a template. The part of route.params.slug contains an array of paths (separated by /) as can be inferred from the file name's spread operator. This catch-all page component can be placed in any directory to limit its scope to that path.

Moreover, although you cannot specify the scope, creating a pages/404.vue page component allows you to specify it as a catch-all route for the entire site.

Dynamic Routing


For simple apps, this may be sufficient, but for practical applications, there are quite a few cases where you want to dynamically map routes using path parameters. The demo app's blog detail page (details.vue) was switching the content of the blog to be displayed using query parameters, but here we change it to path parameters.

  • Before change: /details?id=1
  • After change: /details/1

To create dynamic routing in Nuxt3, configure it like this:

├── index.vue
└── details
    └── [id].vue

While Nuxt2 used an underscore as in pages/details/_id.vue, Nuxt3 has changed to enclose path parameters with [] as in pages/details/[id].vue. This style applies not only to file names but also to directory names.

With this configuration, Nuxt creates the following mappings:

  • / -> pages/index.vue (unchanged)
  • /details/:id -> pages/details/[id].vue

The second definition becomes Vue Router's dynamic routing (id parameter).

We modify index.vue to use this path parameter.

<script setup lang="ts">
const { data: articles, refresh } = await useFetch('/api/blogs');

    <p>New Articles!!</p>
      <li v-for="article in articles" :key="">
        <!-- Changed below -->
        <NuxtLink :to="`/details/${}`">{{ article.title }}</NuxtLink>
        <!-- Also possible
        <NuxtLink :to="{ name: 'details-id', params: { 'id': }}">{{ article.title }}</NuxtLink>
    <button @click="refresh()">Fetch Latest Information</button>
    <Advertisement />

The to property of NuxtLink has been changed to a path parameter. The details/[id].vue side will look like this:

<script setup lang="ts">
const route = useRoute();
// Retrieve id from path parameter
const { id } = route.params;
const { data: article } = await useFetch(`/api/blogs/${id}`);

    <article v-if="article">
      <p>Title: {{ article.title }}</p>
      <hr />
      <div style="width: 500px">{{ article.content }}</div>
    <NuxtLink to="/">Back</NuxtLink>
    <Advertisement />

It's basically the same as details.vue, but the part that was obtained as query parameters (route.query) has been changed to path parameters (route.params). With this, building the Nuxt application will enable page transitions using path parameters.

Custom Mapping Rules


So far, we have looked at Nuxt's file system-based routing, but it is also possible to create custom mapping rules.

  • [Nuxt Documentation - Router Options]

Here, we make it possible to access /foo/bar (pages/foo/bar.vue) based on Nuxt's file system-based routing also with /foo/baz.

To do this, place a router.options.ts in the app directory at the project root.

import type { RouterOptions } from '@nuxt/schema'
import { RouterOptions as VueRouterOptions } from "vue-router";

export default <RouterOptions> {
  routes(_routes: VueRouterOptions['routes']) {
    return [..._routes, {
      path: '/foo/baz',
      component: () => import('~/pages/foo/bar.vue')

The argument to the routes function is the file system-based route mapping created by Nuxt. Here, in addition, we added the route for /foo/baz. The way to describe custom routes is based on Vue Router. For details, see [Vue Router's official documentation].

This allows /foo/baz to map to pages/foo/bar.vue, and page transitions with custom paths are possible via NuxtLink or navigateTo.

  • /foo/bar -> pages/foo/bar.vue (Nuxt default)
  • /foo/baz -> pages/foo/bar.vue (Added custom route)

Of course, you can also modify the above logic to exclude the default route provided by Nuxt.


In addition to directly extending Vue Router, there is also a method using Nuxt Hook (pages:extend). For details, see the official documentation below.

  • [Nuxt Doc - Custom Routing - Pages Hook]



This time, we looked at an overview of routing provided by Nuxt. With Nuxt's default file system-based routing, mapping descriptions are unnecessary, and it can accommodate various use cases.

Next time, we plan to focus on configuration information used in Nuxt applications.