Pages and File Routing

File-based routing in pages/, dynamic routes, layouts, and navigating with NuxtLink.

File-based routing in pages/

Each .vue file inside pages/ becomes a route, matching its path within the folder to a URL:

Plaintext
pages/
├── index.vue          →  /
├── about.vue           →  /about
└── users/
    ├── index.vue       →  /users
    └── [id].vue        →  /users/:id
HTML
<!-- pages/index.vue -->
<template>
  <h1>Welcome home</h1>
</template>

Dynamic routes

A filename wrapped in square brackets captures a dynamic URL segment, read with the useRoute() composable:

HTML
<!-- pages/users/[id].vue -->
<script setup lang="ts">
const route = useRoute();
const userId = route.params.id; // e.g. "42" for /users/42
</script>

<template>
  <h1>User {{ userId }}</h1>
</template>

A catch-all works the same way it does across most file-based routers — pages/shop/[...slug].vue matches /shop/a, /shop/a/b, and so on, with route.params.slug as an array of the matched segments.

Layouts

A layout is a shared shell — navigation, footer, page structure — that wraps page content via a <slot />. The file layouts/default.vue applies automatically to every page that doesn't specify otherwise:

HTML
<!-- layouts/default.vue -->
<template>
  <div>
    <nav>My Site</nav>
    <slot />
    <footer>© 2026</footer>
  </div>
</template>

A page opts into a different, named layout with definePageMeta:

HTML
<!-- layouts/admin.vue -->
<template>
  <div class="admin-shell">
    <aside>Admin sidebar</aside>
    <slot />
  </div>
</template>
HTML
<!-- pages/admin/dashboard.vue -->
<script setup lang="ts">
definePageMeta({ layout: 'admin' });
</script>

<template>
  <h1>Dashboard</h1>
</template>

<NuxtLink> replaces a plain <a> tag for internal navigation — it renders an actual <a> under the hood, but handles client-side routing and automatically prefetches the linked page:

HTML
<template>
  <nav>
    <NuxtLink to="/">Home</NuxtLink>
    <NuxtLink to="/about">About</NuxtLink>
    <NuxtLink :to="`/users/${userId}`">View user</NuxtLink>
  </nav>
</template>

Common mistakes

  • Forgetting <NuxtPage /> in app.vue — the single most common reason a fresh Nuxt project's routes appear to "not work."
  • Using a plain <a href="..."> for internal links instead of <NuxtLink>, losing client-side navigation and prefetching.
  • Expecting a custom layout to apply automatically the way default.vue does — any layout other than default must be explicitly selected per-page with definePageMeta({ layout: '...' }).