Pages and Routes
While adding product detail pages to MegaShop, Bob manually configured 50 vue-router routes. Every time a new product was listed, he had to edit the route files—it was a real pain. Charlie told him that Nuxt 3 automatically generates routes based on file conventions, so creating a file is the same as creating a route.
1. What You'll Learn
- File Routing: Complete rules for routing to the
pages/directory - Dynamic Routing: Parameter Capture in [id].vue and [...slug].vue
- Nested Routes and Layouts: Parent Page + Nested Child Slots
- Programmatic navigation: navigateTo() / useRouter() / useRoute()
- Hands-On Guide to Multi-Level Dynamic Routing in MegaShop
2. A True Story of an Administrator
(1) Pain Point: The Nightmare of Manual Routing Configuration
Bob set up 1,000 routes for MegaShop using the traditional Vue Router—one for each category and each product page. Every time a new product was listed, he had to edit the route file; if he missed even one, it resulted in a 404 error. Alice couldn't find the products, and the complaint rate skyrocketed.
(2) Solutions for Nuxt File Routing
With Nuxt 3's file-based routing, Bob just needs to create a file:
pages/products/[id].vue → /products/:id
One million products in a single file, with dynamic parameters automatically passed through.
(3) Benefits: Zero-Configuration Routing
Bob no longer has to write route configurations—adding a new page is as simple as creating a new file, and 1 million product detail pages require just one [id].vue.
3. File Routing Rules
(1) Directory-to-Route Mapping Process
flowchart LR
A[pages/index.vue] -->|"/"| B[Root Route]
C[pages/about.vue] -->|"/about"| C2[About Route]
D[pages/products/index.vue] -->|"/products"| D2[Product List Route]
E[pages/products/[id].vue] -->|"/products/:id"| E2[Product Detail Route]
F[pages/categories/[...slug].vue] -->|"/categories/:slug*"| F2[Catch-all Route]
(2) File Routing Mapping Rules
| File Path | Generated Route | Description |
|---|---|---|
| pages/index.vue | / | Home |
| pages/about.vue | /about | Static page |
| pages/products/index.vue | /products | Product List |
| pages/products/[id].vue | /products/:id | Dynamic Parameter |
| pages/categories/[slug].vue | /categories/:slug | Dynamic Parameters |
| pages/categories/[...slug].vue | /categories/:slug(*) | Wildcard route |
| pages/404.vue | /404 | Custom 404 |
(1) ▶ Example: Basic Page Routing
<!-- pages/index.vue -->
<template>
<div>
<h1>MegaShop - Millions of Products</h1>
<NuxtLink to="/products">Browse Products</NuxtLink>
</div>
</template>
Output:
// Execution Successful
<!-- pages/about.vue -->
<template>
<div>
<h1>About MegaShop</h1>
<p>Serving over 1 million products worldwide</p>
</div>
</template>
4. Dynamic Routing
(1) Single-Parameter Dynamic Routing
Use the [param] syntax to define dynamic routing parameters.
(1) ▶ Example: Dynamic Routing for Product Detail Pages
<!-- pages/products/[id].vue -->
<template>
<div>
<h1>Product #{{ route.params.id }}</h1>
<p>Price: ${{ product?.price }} USD</p>
</div>
</template>
<script setup lang="ts">
const route = useRoute()
const { data: product } = await useFetch(`/api/products/${route.params.id}`)
</script>
Output:
// Execution Successful
(2) Wildcard Routes
Use the [...slug] syntax to capture multi-level paths.
(2) ▶ Example: Multi-level Categorized Wildcard Routing
<!-- pages/categories/[...slug].vue -->
<template>
<div>
<h1>Category: {{ slugPath }}</h1>
<p>{{ products.length }} products found</p>
</div>
</template>
<script setup lang="ts">
const route = useRoute()
// /categories/electronics/headphones → slug = ['electronics', 'headphones']
const slugPath = computed(() => (route.params.slug as string[]).join('/'))
const { data: products } = await useFetch(`/api/categories/${slugPath.value}`)
</script>
Output:
// Execution Successful
(3) Dynamic Routing Parameter Types
| Syntax | Match | Example URL | params value |
|---|---|---|---|
| [id] | Single section | /products/123 | { id: '123' } |
| [id].vue + index.vue | Optional | /products or /products/123 | { id?: '123' } |
| [...slug] | Multi-segment wildcard | /a/b/c | { slug: ['a','b','c'] } |
5. Nested Routes
(1) Nested Routing Structure
Nested routing requires a parent page (a .vue file with the same name) and a child directory to work together:
pages/
├── products/
│ ├── index.vue # /products (child)
│ ├── [id].vue # /products/:id (child)
│ └── edit.vue # /products/edit (child)
└── products.vue # /products (parent with <NuxtPage />)
(1) ▶ Example: Nested Routes on a Product Page
<!-- pages/products.vue - Parent layout -->
<template>
<div>
<nav class="product-nav">
<NuxtLink to="/products">All Products</NuxtLink>
<NuxtLink to="/products/featured">Featured</NuxtLink>
</nav>
<!-- Child routes render here -->
<NuxtPage />
</div>
</template>
Output:
// Execution Successful
<!-- pages/products/index.vue - Child: product list -->
<template>
<div>
<h1>All Products</h1>
<p>1 million products available</p>
</div>
</template>
<!-- pages/products/[id].vue - Child: product detail -->
<template>
<div>
<h1>Product Detail</h1>
<p>ID: {{ route.params.id }}</p>
</div>
</template>
(2) Comparison of Nested Routes vs. Layout
| Dimension | Nested Routing | Layout |
|---|---|---|
| Mechanism | Parent page + NuxtPage slot | layouts/ + NuxtLayout |
| Routing Relationships | Parent-Child Routes Sharing Layouts | Page-Level Layout Switching |
| Data Retrieval | Parent and child each use useFetch |
Layout does not retrieve data |
| Use Cases | Category → Product → Review Hierarchy | Overall Page Layout (Header/Footer) |
6. Programmatic Navigation
(1) Comparison of Three Navigation Methods
| API | Purpose | Available on the server | Use Case |
|---|---|---|---|
| navigateTo() | Programmatic navigation | ✅ | Redirect after login |
| useRouter().push() | Client-side navigation | ❌ | Navigation via button click |
| NuxtLink | Declarative Links | ✅ | Navigation in Templates |
(1) ▶ Example: navigateTo Server-Side Redirect
// In middleware or server-side context
export default defineNuxtRouteMiddleware((to) => {
const isAuthenticated = useState('isAuthenticated')
if (!isAuthenticated.value && to.path.startsWith('/admin')) {
return navigateTo('/login', { redirectCode: 302 })
}
})
Output:
// Execution Successful
(2) ▶ Example: useRouter client-side navigation
<template>
<button @click="goToProduct(product.id)">
View Details
</button>
</template>
<script setup lang="ts">
const router = useRouter()
function goToProduct(id: number) {
router.push(`/products/${id}`)
}
</script>
Output:
// Execution Successful
(3) ▶ Example: Retrieving parameters with useRoute
<script setup lang="ts">
const route = useRoute()
// Access params
const productId = route.params.id // From [id].vue
const categorySlug = route.params.slug // From [...slug].vue
// Access query params
// /products?category=electronics&sort=price
const category = route.query.category // 'electronics'
const sort = route.query.sort // 'price'
</script>
Output:
// Execution Successful
7. Hands-On Guide to Multi-Level Routing in MegaShop
(1) MegaShop Routing Plan
| URL | File | Function |
|---|---|---|
| / | pages/index.vue | Home |
| /products | pages/products/index.vue | Product List |
| /products/[id] | pages/products/[id].vue | Product Details |
| /categories/[category] | pages/categories/[category].vue | Category Page |
| /categories/[category]/[subcategory] | pages/categories/[...slug].vue | Subcategory |
| /cart | pages/cart.vue | Shopping Cart |
| /checkout | pages/checkout.vue | Checkout Page |
| /admin | pages/admin/index.vue | Admin Dashboard |
| /admin/products | pages/admin/products/index.vue | Product Management |
| /404 | pages/404.vue | 404 Page |
(1) ▶ Example: MegaShop 404 Page
<!-- pages/404.vue -->
<template>
<div class="not-found">
<h1>404 - Product Not Found</h1>
<p>The product you're looking for doesn't exist.</p>
<NuxtLink to="/products">Browse all products</NuxtLink>
</div>
</template>
Output:
// Execution Successful
8. Comprehensive Example: The MegaShop Routing System
<!-- pages/index.vue - Homepage -->
<template>
<div>
<section class="hero">
<h1>MegaShop</h1>
<p>Over 1 million products, delivered worldwide</p>
<NuxtLink to="/products">Shop Now</NuxtLink>
</section>
<section class="featured">
<ProductCard v-for="p in featured" :key="p.id" :product="p" />
</section>
</div>
</template>
<script setup lang="ts">
const { data: featured } = await useFetch('/api/products/featured')
</script>
<!-- pages/products/[id].vue - Product Detail -->
<template>
<div v-if="product">
<h1>{{ product.name }}</h1>
<p class="price">${{ product.price }} USD</p>
<button @click="addToCart">Add to Cart</button>
<button @click="goBack">Back to List</button>
</div>
<div v-else>
<p>Product not found</p>
<NuxtLink to="/products">Browse Products</NuxtLink>
</div>
</template>
<script setup lang="ts">
const route = useRoute()
const router = useRouter()
const { data: product } = await useFetch(`/api/products/${route.params.id}`)
function addToCart() {
navigateTo('/cart')
}
function goBack() {
router.back()
}
</script>
❓ FAQ
Number(route.params.id), or let the API's return type determine the value when using useFetch.slug is an array. Catch-all routes are suitable for categories with an unlimited number of levels.products.vue and the subdirectory products/ exist. If you only want the child route, do not create the parent page.a tag?a tag triggers a full page refresh. Use NuxtLink for internal navigation and the a tag for external links.pages/404.vue file. Nuxt 3 automatically displays this page when no route matches the request. You can also handle it in a catch-all route.routeRules and routes?routeRules are configured in nuxt.config.ts to specify rendering strategies (SSR/ISR/CSR) based on route paths; they do not affect route definitions. Routes are defined by the files in the pages/ directory; routeRules only control rendering behavior.📖 Summary
- Nuxt 3 File Routing: Files under
pages/are automatically mapped to routes with zero configuration - Dynamic routing uses [id].vue to capture a single parameter and [...slug].vue to capture multi-level paths
- Nested routes require a parent page with the same name + a NuxtPage slot; this is a different concept from Layout.
- Programmatic navigation: Use
navigateTo()on the server anduseRouter().push()on the client - MegaShop: A Million Products in Just One [id].vue File
📝 Exercises
- Basic Exercise (Difficulty: ⭐): Create pages/about.vue and pages/contact.vue, and verify that the routes are generated automatically.
- Advanced Problem (Difficulty ⭐⭐): Implement
/products/[category]/[id]two-level dynamic routing and display the "category" and "id" parameters on the page. - Challenge (Difficulty: ⭐⭐⭐): Implement a catch-all route [...slug].vue that can render multi-level paths such as
/docs/getting-started/installationand display a breadcrumb navigation bar.
---|



