Dynamic Routes — [slug]

Square Brackets Capture Path Segments

Dynamic Routes — [slug]

A filename in square brackets becomes a route parameter — read it with useRoute().params.

4 min read Level 2/5 #nuxt#routing#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.

Catch-All Routes →