Generate pages from data with [param] files, read the value with useRoute, and match many segments with catch-all routes.
Why: you don’t hand-write a page per blog post. Name a file (or folder) with square brackets, like [slug].vue, and it serves every value. Note: read the value with useRoute().params — useRoute is auto-imported.
<!-- app/pages/blog/[slug].vue -> /blog/anything -->
<script setup lang="ts">
const route = useRoute()
</script>
<template>
<h1>Post: {{ route.params.slug }}</h1>
</template>Why: a [...slug].vue file matches any number of segments, so one file serves /docs, /docs/a, and /docs/a/b/c. Useful for docs and file-tree style URLs. Note: the captured value is an array of segments.
<!-- app/pages/docs/[...slug].vue -->
<!-- matches /docs/intro, /docs/api/use-fetch, ... -->
<script setup lang="ts">
const route = useRoute()
</script>
<template>
<p>Path: {{ (route.params.slug as string[]).join('/') }}</p>
</template>Why: reject bad URLs early. definePageMeta’s validate returns false for invalid params, which makes Nuxt show the 404 page instead of rendering a broken one. Here only numeric ids are allowed.
<!-- app/pages/products/[id].vue -->
<script setup lang="ts">
definePageMeta({
validate: (route) => /^\d+$/.test(route.params.id as string),
})
const route = useRoute()
</script>
<template>
<h1>Product #{{ route.params.id }}</h1>
</template>