Nuxt Pages

One of the standout features of Nuxt is its powerful file-system based routing. Unlike traditional Vue applications that require manual route mapping within a vue-router configuration file, Nuxt automatically generates corresponding routes by scanning the pages/ directory at the root of your project.

This mechanism significantly streamlines the development workflow, allowing developers to manage the application's URL architecture intuitively through the file structure. Simply create a file inside the pages/ directory, and Nuxt instantly converts it into an accessible page.
Mapping pages in Nuxt follows a straightforward file-naming convention.

Static Routes

The simplest form of routing is the static route. For example, if you create the following files under the pages/ directory:

  • pages/index.vue $\rightarrow$ maps to / (root path)
  • pages/about.vue $\rightarrow$ maps to /about
  • pages/contact.vue $\rightarrow$ maps to /contact

Example code (pages/about.vue):

<template>
  <div>
    <h1>About Us</h1>
    <p>This is a static page automatically generated by Nuxt.</p>
  </div>
</template>

Directory-Level Routing

To organize related pages neatly, you can create subdirectories. For instance:

  • pages/blog/index.vue $\rightarrow$ maps to /blog
  • pages/blog/first-post.vue $\rightarrow$ maps to /blog/first-post

Dynamic Routes

In real-world applications, you frequently need to handle pages that share a template but display different content (such as article detail pages or user profiles). Nuxt handles dynamic routing parameters by utilizing square brackets [].

Single-Parameter Dynamic Routes

By creating a file named pages/posts/[id].vue, the [id] segment acts as a placeholder matching any value. Visiting either /posts/1 or /posts/hello-world will render this component.

Inside the component, you can retrieve the URL parameters using the useRoute() composable:

Example code (pages/posts/[id].vue):

<script setup>
const route = useRoute()
// Retrieve the id parameter from the URL
const postId = route.params.id
</script>

<template>
  <div>
    <h1>Article Details</h1>
    <p>Currently viewing article ID: {{ postId }}</p>
  </div>
</template>

Catch-all Routes

If you need to build a custom 404 error page or handle deeply nested, unpredictable paths, you can use [...slug].vue. This will capture all paths that are not matched by other defined routes.

Nested Routes

Nested routes allow you to embed one page inside another, which is extremely useful for building complex layouts featuring sidebars or top navigation bars.

Implementation Steps

  1. Create a Folder: Create a directory with the same name as the parent route.
  2. Create the Parent Component: Place a .vue file alongside the newly created folder.
  3. Create Child Components: Place .vue files inside the folder.
  4. Use <NuxtPage />: Insert the <NuxtPage /> component inside the parent component to act as a slot for child pages.

Directory Structure Example:

pages/
├── parent.vue
└── parent/
    ├── child1.vue
    └── child2.vue

Parent Component Code (pages/parent.vue):

<template>
  <div class="parent-layout">
    <nav>
      <NuxtLink to="/parent/child1">Child Page 1</NuxtLink> | 
      <NuxtLink to="/parent/child2">Child Page 2</NuxtLink>
    </nav>
    <hr />
    <!-- Child pages will render here -->
    <NuxtPage />
  </div>
</template>

With this setup, navigating to /parent/child1 renders parent.vue as the structural wrapper, while child1.vue is rendered right inside the <NuxtPage /> placeholder.

In Nuxt, relying on native <a> tags for internal navigation is strongly discouraged because they trigger a full-browser page refresh, destroying single-page application (SPA) performance advantages.

Using <NuxtLink>

<NuxtLink> is a built-in component provided by Nuxt that wraps around vue-router under the hood. Its key benefits include:

  • Client-side Navigation: Updates only the page content without reloading the entire application.
  • Smart Prefetching: Automatically preloads target page code as soon as the <NuxtLink> enters the viewport, making transitions feel instantaneous.

Example:

<template>
  <nav>
    <NuxtLink to="/">Home</NuxtLink>
    <NuxtLink to="/about">About</NuxtLink>
    <NuxtLink :to="`/posts/${postId}`">Read Article</NuxtLink>
  </nav>
</template>

Programmatic Navigation

Within your JavaScript or TypeScript logic, you can trigger navigation using the navigateTo utility function:

<script setup>
const goToProfile = async () => {
  await navigateTo('/profile')
}
</script>

Page Metadata and Middleware

To optimize SEO or enforce access control, Nuxt offers straightforward APIs to manage page-level metadata and route guards.

Defining Page Titles and Descriptions

Using useHead or useSeoMeta, you can independently configure HTML header information for each page:

<script setup>
useHead({
  title: 'My Blog - Home',
  meta: [
    { name: 'description', content: 'Welcome to my technical blog built with Nuxt' }
  ]
})
</script>

Route Middleware

If you need to verify authentication status before a user can access a specific page, you can define middleware using definePageMeta:

<script setup>
definePageMeta({
  middleware: 'auth' // Assumes you have defined auth.ts inside the middleware/ directory
})
</script>

Conclusion

Nuxt's page system embraces the principle of convention over configuration, seamlessly unifying route management with the project's file structure. From simple static views to sophisticated dynamic and nested routes, developers can achieve robust architectures simply through organized folder hierarchies. Combined with the prefetching capabilities of <NuxtLink> and parameter handling via useRoute, building high-performance, maintainable modern web applications becomes both effortless and efficient.