Square Brackets Capture Path Segments
Dynamic Routes — [slug]
A filename in square brackets becomes a route parameter — read it with useRoute().params.
What you'll learn
- Create pages/blog/[slug].vue
- Read params with useRoute().params
- Combine multiple params into nested routes
Static routes only get you so far. For blog posts, user profiles, and product pages you need dynamic segments — Nuxt expresses them with square brackets in the filename.
A Dynamic Blog Post
Create pages/blog/[slug].vue and any URL like /blog/hello-world matches it.
<!-- pages/blog/[slug].vue -->
<script setup lang="ts">
const route = useRoute()
const slug = route.params.slug as string
</script>
<template>
<article>
<h1>Post: {{ slug }}</h1>
</article>
</template> route.params.slug is the literal string from the URL. The pattern works for any segment depth.
Multiple Params
You can chain dynamic segments and folders freely. A page for a comment under a post:
pages/blog/[slug]/comments/[commentId].vue
# matches /blog/hello-world/comments/42 <script setup lang="ts">
const { slug, commentId } = useRoute().params as {
slug: string
commentId: string
}
</script> Linking to Dynamic Pages
Use NuxtLink with either a path string or an object:
<template>
<NuxtLink :to="`/blog/${post.slug}`">{{ post.title }}</NuxtLink>
<!-- equivalently -->
<NuxtLink :to="{ path: `/blog/${post.slug}` }">
{{ post.title }}
</NuxtLink>
</template> Params from a dynamic route stay typed as string by default. The next lesson on catch-all routes shows what to do when you need to match more than one segment.