Encodera

Thanks for reading!

Donate to the site — we work hard for you.

Contents

Dynamic routes

Each blog post has its own address: /posts/1, /posts/2 and so on. You cannot create a file for every post, so part of the address becomes a parameter, a variable whose value comes from the address. In a file name a parameter is written in square brackets.

The post page

Create the page with a command:

npx nuxi add page "posts/[id]"

The file app/pages/posts/[id].vue appears. It serves every address of the form /posts/anything. Replace its content:

<!-- app/pages/posts/[id].vue -->
<script setup lang="ts">
const route = useRoute()
</script>
 
<template>
  <section>
    <h2>Post number {{ route.params.id }}</h2>
  </section>
</template>

useRoute() returns the current route, and route.params.id is the value of the id parameter. Open /posts/7 and the page shows "Post number 7".

Finding a post by number

While posts are kept in an array, find the right one:

<!-- app/pages/posts/[id].vue -->
<script setup lang="ts">
const posts = [
  { id: 1, title: 'First post', text: 'Hello, world!' },
  { id: 2, title: 'Learning Nuxt', text: 'Pages from files.' }
]
 
const route = useRoute()
const post = computed(() => posts.find((p) => p.id === Number(route.params.id)))
</script>
 
<template>
  <article>
    <template v-if="post">
      <h2>{{ post.title }}</h2>
      <p>{{ post.text }}</p>
    </template>
    <p v-else>Post not found.</p>
  </article>
</template>

At /posts/2 the post "Learning Nuxt" appears, and at /posts/99 you see "Post not found."

Linking to a post

In the list app/pages/posts/index.vue, turn the titles into links:

<!-- app/pages/posts/index.vue -->
<h3>
  <NuxtLink :to="`/posts/${post.id}`">{{ post.title }}</NuxtLink>
</h3>

The address is built from the post number. Clicking a title opens its page.

Other kinds of parameters

FileMatches addressesParameter value
posts/[id].vue/posts/5'5'
[category]/[slug].vue/vue/propstwo parameters
archive/[[year]].vue/archive and /archive/2024the parameter is optional
docs/[...slug].vue/docs/a/b/cthe array ['a', 'b', 'c']

Double brackets make a parameter optional, and the ellipsis collects all remaining parts of the address.

Validating a parameter

To open the page only for numbers, add a check to definePageMeta:

<!-- app/pages/posts/[id].vue -->
<script setup lang="ts">
definePageMeta({
  validate: (route) => /^\d+$/.test(String(route.params.id))
})
</script>

If the function returns false, Nuxt shows a 404 error: the address /posts/abc no longer opens the post page.

Common mistake

Parameters from the address are always strings. The comparison p.id === route.params.id never matches, because 1 and '1' are different values for ===. Convert the parameter to a number before comparing: Number(route.params.id).

Thanks for reading!

Donate to the site — we work hard for you.