Catching dynamic routes with rest parameters
Catch-all routes in SvelteKit use rest parameters to match URLs at any depth, making them invaluable for building flexible navigation structures. Understanding rest parameters in routes—their capabilities and trade-offs—helps you build scalable documentation sites, CMS platforms, and content hierarchies without exploding your route count. We'll walk through practical patterns that handle real-world complexity while avoiding common pitfalls.
How [...path] captures any depth
Rest parameters in SvelteKit routes use the [...name] syntax to capture one or more path segments as an array. When a user visits /docs/getting-started/installation, the rest parameter collects everything after the route segment into a single variable.
Here's a minimal example structure:
// src/routes/docs/[...path]/+page.svelte
<script>
export let data;
console.log(data.path); // ['getting-started', 'installation']
</script>
<h1>{data.title}</h1>
<article>{@html data.content}</article>
In the server-side loader, you receive the path as an array string and resolve content accordingly:
// src/routes/docs/[...path]/+page.server.ts
import { error } from '@sveltejs/kit';
export async function load({ params }) {
const { path } = params;
const slug = path.join('/');
const doc = await fetchDocBySlug(slug);
if (!doc) {
throw error(404, 'Documentation page not found');
}
return {
path,
title: doc.title,
content: doc.html,
};
}
The rest parameter automatically becomes a string in params.path and is split by the framework. Unlike optional segments or multiple bracket pairs, rest parameters are greedy—they consume all remaining segments in one go. This makes them perfect for hierarchical content where depth is unknown at build time.
Useful for docs and CMS pages
Documentation sites and content management systems benefit most from rest parameters because their navigation hierarchies are often deep, irregular, and frequently updated. Rather than defining routes like /docs/category/[subcategory]/[article], you can use a single catch-all that handles any nesting level.
Consider a real-world documentation site with this structure:
/docs/guides/setup/docs/guides/installation/windows/docs/guides/installation/macos/arm64/docs/api/rest/authentication/docs/api/graphql/queries/users
A rest parameter route handles all of these without modification. Your content backend—whether a CMS API, headless service, or file system—becomes the source of truth for structure.
// src/routes/docs/[...path]/+page.server.ts
import { error } from '@sveltejs/kit';
import { getDocFromCMS } from '$lib/cms';
export async function load({ params }) {
const slug = params.path.join('/');
try {
const doc = await getDocFromCMS(slug);
return {
path: params.path,
title: doc.title,
content: doc.content,
breadcrumbs: generateBreadcrumbs(params.path),
};
} catch (err) {
if (err.status === 404) {
throw error(404, 'Page not found');
}
throw error(500, 'Failed to load documentation');
}
}
function generateBreadcrumbs(pathSegments: string[]) {
return pathSegments.map((segment, index) => ({
label: segment.charAt(0).toUpperCase() + segment.slice(1),
href: '/' + pathSegments.slice(0, index + 1).join('/'),
}));
}
This pattern scales elegantly. A CMS editor adds new pages without touching your code. Your routing adapts instantly to the content structure, and you maintain one page component instead of managing nested route hierarchies.
Combine with prerender entries
Prerendering catch-all routes requires explicit configuration. Because rest parameters can match infinite paths, SvelteKit can't automatically discover which URLs to prerender. You must provide an explicit list via the entries option in the prerender configuration.
Here's how to prerender a documentation site:
// svelte.config.js
import adapter from '@sveltejs/adapter-auto';
export default {
kit: {
adapter: adapter(),
prerender: {
entries: async () => {
const docs = await fetchAllDocPaths(); // Call your CMS or file system
return docs.map(doc => `/docs/${doc.slug}`);
},
},
},
};
The entries function is called once during the build. Fetch all your content paths and return the full URLs you want prerendered. This happens before the actual build runs, giving SvelteKit a complete sitemap to process.
// src/lib/cms.ts
export async function getAllDocPaths(): Promise<string[]> {
const response = await fetch('https://api.example.com/docs/all-paths');
const docs = await response.json();
return docs.map(doc => doc.slug);
}
In production, this approach ensures your static site is fully populated at build time. If you're using an on-demand ISR adapter (like Vercel's), the prerender entries provide a warm start for commonly accessed pages while remaining paths are generated as needed.
A key trade-off: prerender entries must be static or deterministic at build time. If your content changes hourly, prerendering is less effective than server-side rendering with caching. For stable documentation or infrequently-updated CMSs, this trade-off favors performance and cost savings.
Return 404 for unknown paths
Rest parameters will match any path at their level and below. Without explicit 404 handling, users can reach non-existent pages. You must validate the requested path and throw a 404 error when content isn't found.
Always validate in your +page.server.ts:
// src/routes/docs/[...path]/+page.server.ts
import { error } from '@sveltejs/kit';
export async function load({ params }) {
const slug = params.path.join('/');
// Query your content source
const doc = await db.select('*').from('docs').where('slug', slug).first();
// Throw 404 if not found
if (!doc) {
throw error(404, `Documentation for "${slug}" not found`);
}
return {
title: doc.title,
content: doc.content,
};
}
When you throw error(404, ...), SvelteKit renders your +error.svelte` component at that route with the 404 status code. Clients receive the correct HTTP status, which is critical for SEO and user experience.
A practical enhancement: log 404s to identify gaps in your documentation or common typos in bookmarked URLs:
export async function load({ params }) {
const slug = params.path.join('/');
const doc = await db.select('*').from('docs').where('slug', slug).first();
if (!doc) {
// Log 404s for monitoring
await logMissingPage(slug, params.request.headers.get('referer'));
throw error(404, `Documentation for "${slug}" not found`);
}
return { title: doc.title, content: doc.content };
}
This feedback loop helps content teams identify undocumented features or popular topics that need expanding.
Common mistakes
- Forgetting to validate paths. Rest parameters match anything, so always check whether the requested path exists in your content store. Missing validation leads to blank pages or confusing errors.
- Assuming rest parameters are arrays in loaders. In
+page.server.ts,params.pathis a string (with segments joined by/). Split or parse it manually if you need an array structure for routing logic. - Prerendering without explicit entries. SvelteKit will not auto-discover paths matched by rest parameters. Builds will succeed but only your root or manually listed paths will be prerendered, leaving dynamic routes server-rendered or missing.
- Over-using rest parameters. If your routes are predictable (e.g., three known levels), define them explicitly with nested routes instead. Rest parameters add flexibility but reduce static analysis and IDE autocompletion.
- Not handling deep nesting in URLs. Very deep paths (e.g., 10+ segments) can hit server limits or create unwieldy breadcrumb trails. Validate path depth in your loader and reject excessive nesting early.
Key takeaways
- Rest parameters
[...path]capture one or more segments as a single variable, enabling flexible catch-all routes for documentation and CMS content. - Use rest parameters when content hierarchy is irregular, deeply nested, or frequently updated—they adapt to your backend without route changes.
- Always validate requested paths and throw 404 errors for missing content; rest parameters are greedy and will match any input unless you check.
- Prerender catch-all routes by providing explicit entries via the
prerender.entriesconfiguration; SvelteKit cannot auto-discover infinite paths. - Monitor 404s to identify gaps in your content and refine your documentation structure based on real user navigation patterns.
- Consider the trade-off between flexibility and predictability: explicit nested routes offer better IDE support and static analysis, while rest parameters scale with content growth.






Comments
Loading comments…