One Route, Many Pages
Creating a folder for every service would be tedious — and impossible if services are added dynamically. SvelteKit solves this with dynamic routes: one route definition that matches multiple URLs based on a parameter.
What You’ll Learn
- Create dynamic routes with
[parameter]syntax - Access route parameters in your component
- Build service detail pages for BookIt
The Problem
BookIt has multiple services. Each needs its own page:
/services/lawn-mowing— Lawn Mowing details/services/hedge-trimming— Hedge Trimming details/services/garden-consultation— Garden Consultation details
Creating a separate folder for each service doesn’t scale. What happens when you have 50 services? 500?
The Solution: Dynamic Parameters
Instead of creating services/lawn-mowing/, services/hedge-trimming/, etc., you create a single folder with a parameter:
src/routes/services/[slug]/
└── +page.svelte The square brackets [slug] tell SvelteKit this segment is dynamic. It will match any value:
/services/lawn-mowing✓/services/hedge-trimming✓/services/anything-at-all✓
The actual value (lawn-mowing, hedge-trimming, etc.) becomes available as a parameter in your component.
Create the Service Detail Route
Step 1: Create the Dynamic Folder
Inside src/routes/services/, create a folder named [slug]:
src/routes/
├── services/
│ ├── +page.svelte (services listing)
│ └── [slug]/ ← Create this folder
│ └── +page.svelte ← And this file The folder name must include the square brackets. It’s literally named [slug].
Step 2: Access the Parameter
Create +page.svelte inside [slug]:
<!-- filename: src/routes/services/[slug]/+page.svelte -->
<script>
let { data } = $props();
</script>
<h1>Service: {data.slug}</h1>
<p>You're viewing the detail page for: {data.slug}</p> Wait — where does data.slug come from? Im getting ERROR on slug! Do not worry about it as we will to load it in next step.
Step 3: Add a Load Function
Dynamic routes typically need a load function to access the parameter. Create +page.js in the same folder:
// filename: src/routes/services/[slug]/+page.js
export function load({ params }) {
return {
slug: params.slug
};
} The params object contains all dynamic segments from the URL. Since our folder is [slug], we access params.slug. We will cover load function later durning the course.
Step 4: Test It
Visit these URLs:
http://localhost:5173/services/lawn-mowinghttp://localhost:5173/services/hedge-trimminghttp://localhost:5173/services/test-123
Each should display “Service: lawn-mowing”, “Service: hedge-trimming”, etc.
Build a Real Detail Page
Displaying just the slug isn’t useful. Let’s show actual service information. We need to simulate a data source. For now, we’ll hardcode service data again but now in in +page.js. Do not forget also return services from load function.
Update +page.js to find the service:
// filename: src/routes/services/[slug]/+page.js
const services = [
{
id: '1',
slug: 'lawn-mowing',
name: 'Lawn Mowing',
description: 'Professional lawn mowing service for residential properties.',
price: 50,
duration: 60
},
{
id: '2',
slug: 'hedge-trimming',
name: 'Hedge Trimming',
description: 'Expert hedge and shrub trimming to keep your garden tidy.',
price: 75,
duration: 90
},
{
id: '3',
slug: 'garden-consultation',
name: 'Garden Consultation',
description: 'One-on-one consultation for garden planning and advice.',
price: 100,
duration: 60
}
];
export function load({ params }) {
const service = services.find(s => s.slug === params.slug);
return {
slug: params.slug,
// Return the found service (or null if not found)
service: service || null
};
} Now update the page to display service details:
<!-- filename: src/routes/services/[slug]/+page.svelte -->
<script>
let { data } = $props();
</script>
{#if data.service}
<h1>{data.service.name}</h1>
<p>{data.service.description}</p>
<dl>
<dt>Price</dt>
<dd>${data.service.price}</dd>
<dt>Duration</dt>
<dd>{data.service.duration} minutes</dd>
</dl>
<button>Book Now</button>
{:else}
<h1>Service Not Found</h1>
<p>Sorry, we couldn't find a service matching "{data.slug}".</p>
{/if} Understanding the Data Flow
Here’s what happens when someone visits /services/lawn-mowing:
- SvelteKit matches the URL to
services/[slug]/+page.svelte - The
loadfunction in+page.jsruns withparams.slug = 'lawn-mowing' loadfinds the service and returns it- The page component receives
data.servicevia props - The template renders the service details
This separation — loading data in +page.js, rendering in +page.svelte — is a core SvelteKit pattern we’ll explore deeply in Module 9.
Your Project Structure
src/routes/
├── +page.svelte → /
├── services/
│ ├── +page.svelte → /services
│ └── [slug]/
│ ├── +page.js ← Load function (new!)
│ └── +page.svelte → /services/* (dynamic)
├── about/
│ └── +page.svelte → /about
└── contact/
└── +page.svelte → /contact Common Mistakes
Forgetting the Square Brackets
src/routes/services/
└── slug/
└── +page.svelte → /services/slug (literal!) Without brackets, slug is treated as a literal path segment. Only /services/slug would match — not /services/lawn-mowing.
src/routes/services/
└── [slug]/
└── +page.svelte → /services/* (any value) Parameter Name Mismatch
// Folder is [slug], but accessing wrong parameter name
export function load({ params }) {
return { id: params.id }; // ❌ undefined — should be params.slug
} The parameter name in params matches the folder name. [slug] gives you params.slug, [id] would give you params.id.
Not Handling Missing Data
Always handle the case where no service matches:
{#if data.service}
<!-- Show service -->
{:else}
<!-- Show error state -->
{/if} Without this check, visiting /services/nonexistent would crash trying to access properties on undefined.
Summary
Dynamic routes let you create a single page definition that handles many URLs. The [parameter] folder syntax captures the URL segment as a value you can use to load specific data.
Key takeaways:
- Square brackets
[name]create dynamic route segments - Access parameters via
params.namein load functions - Always handle cases where the parameter doesn’t match valid data
Next Steps
We have multiple pages now, but no way to navigate between them without typing URLs. Continue with Link All Pages Together to add proper navigation throughout BookIt.