Dynamic Routes
Using [param] folders to match variable URL segments, and reading them with page.params.
阅读需 2 分钟
Most real routes aren't fixed strings like /about — a blog needs /blog/my-first-post, /blog/another-post, and every future post, all handled by one route definition. SvelteKit does this with square-bracket folder names.
Defining a dynamic segment
src/routes/
└── blog/
└── [slug]/
└── +page.svelte → /blog/anything-here
[slug] matches any single URL segment, and the matched value is available to your component as a route parameter. The folder name inside the brackets (slug) becomes the key you read it by.
Reading the parameter
<!-- src/routes/blog/[slug]/+page.svelte -->
<script>
import { page } from '$app/state';
</script>
<h1>Post: {page.params.slug}</h1>Visiting /blog/hello-world renders "Post: hello-world". page.params holds every dynamic segment matched for the current route, keyed by folder name — but reading it directly in the component is only useful for trivial cases. Almost always you want to use that parameter to fetch the actual post, which means passing it to a load function instead (covered next lesson) rather than fetching inside the component itself.
Multiple and nested parameters
Segments combine naturally:
src/routes/
└── shop/
└── [category]/
└── [product]/
└── +page.svelte → /shop/shoes/red-sneakers
<script>
import { page } from '$app/state';
let { category, product } = $derived(page.params);
</script>
<p>{category} / {product}</p>Optional and rest parameters
Two variations handle less common shapes. A rest parameter ([...path]) matches any number of remaining segments, useful for things like a file browser or a catch-all:
src/routes/
└── docs/
└── [...path]/
└── +page.svelte → /docs/a, /docs/a/b, /docs/a/b/c
An optional parameter ([[lang]]) matches zero or one segment, useful for an optional locale prefix:
src/routes/
└── [[lang]]/
└── +page.svelte → / and /fr both match
Validating parameters with matchers
Not every string should count as a valid id. A matcher constrains what a dynamic segment accepts, so /blog/abc can 404 if you only want numeric post IDs:
// src/params/integer.js
/** @param {string} param */
export function match(param) {
return /^\d+$/.test(param);
}src/routes/
└── blog/
└── [id=integer]/
└── +page.svelte → matches /blog/42, not /blog/abc
Matchers keep routing decisions in the routing layer instead of scattering if (isNaN(id)) checks through your components — a request that doesn't match falls through to the next matching route, or a 404, before your page code ever runs.
The next lesson puts these parameters to real use: fetching data based on them with load functions, which is how a dynamic route actually becomes a page with real content instead of just an echoed URL segment.