SvelteKit Routing

Chapter 11
SvelteKit Routing
File-based routing — the folder structure IS the route map

Routing in the Svelte world is provided by SvelteKit, its official meta-framework — the equivalent of Next.js for React or Nuxt for Vue (and parallel to the SSR that Angular ships built-in). The standout feature: routing is file-based. You don't write a route config array (React Router, Vue Router) — instead, the folder structure under src/routes is the route map. This is the same model as Next.js's App Router, which Project 7 of the React course touched on.

Starting a SvelteKit project
The bare Vite template used so far is component-only. A SvelteKit app is created with npx sv create my-app (choosing the SvelteKit option), which scaffolds the src/routes directory and the dev server. Everything you've learned about Svelte components applies unchanged — SvelteKit just adds the routing, data-loading, and SSR layer around them.

Pages Are +page.svelte Files

src/routes/ ├── +page.svelte // the "/" route ├── about/ │ └── +page.svelte // the "/about" route └── contact/ └── +page.svelte // the "/contact" route

Each folder under src/routes is a URL segment, and a +page.svelte file inside it is the page rendered for that URL. The root +page.svelte is /, about/+page.svelte is /about, and so on. The + prefix marks SvelteKit's special files (distinguishing them from your own components). No route config to maintain — adding a page is creating a file.

Layouts — Shared UI Across Routes

<!-- src/routes/+layout.svelte --> <script> let { children } = $props(); </script> <nav> <a href="/">Home</a> <a href="/about">About</a> </nav> {@render children()} <!-- the current page renders here -->

A +layout.svelte file wraps every page in its folder (and subfolders) — perfect for a shared nav, header, or footer. The current page renders where you call {@render children()} (the snippet mechanism from Chapter 8). This is SvelteKit's equivalent of a layout route with <Outlet> (React Router / Vue Router's <RouterView>), but defined by file convention rather than configuration.

Navigation — Just Anchor Tags

<a href="/about">About</a>

A genuinely nice surprise: SvelteKit navigates with plain <a> tags. There's no <Link> (React Router) or <RouterLink> (Vue) component — SvelteKit intercepts ordinary anchor clicks and turns them into client-side navigation automatically (no full page reload), while still working as a real link if JS is unavailable. The warning every other framework needed ("don't use a plain <a>!") is simply inverted here: plain anchors are the correct way.

Dynamic Routes — [param] Folders

src/routes/ └── product/ └── [id]/ └── +page.svelte // matches /product/anything

A folder named in square brackets — [id] — is a dynamic segment, matching any value in that URL position (/product/1, /product/42). The param is read inside the page from the page state:

<!-- src/routes/product/[id]/+page.svelte --> <script> import { page } from '$app/state'; const id = $derived(page.params.id); </script> <p>Showing product {id}</p>

page.params.id reads the [id] segment — the equivalent of React Router's useParams(), Vue Router's route.params, and Angular's ActivatedRoute.paramMap. Wrapping it in $derived keeps it reactive, so navigating from /product/1 to /product/2 updates it. (The $app/state import is SvelteKit-provided.)

Programmatic Navigation

import { goto } from '$app/navigation'; function openProduct(id) { goto(`/product/${id}`); }

goto(url) navigates from code — after a form submits, a login succeeds, an action completes — the equivalent of React Router's useNavigate(), Vue's router.push(), and Angular's Router.navigate(). Imported from $app/navigation.

Other special route files exist — and a catch-all
SvelteKit has more + files than just +page.svelte: +layout.svelte (covered), +error.svelte (an error boundary for a route), and +page.js/+page.server.js for data loading (next chapter). A catch-all dynamic segment uses [...rest] (e.g. [...path]/+page.svelte) — the equivalent of a wildcard route. The file-based system trades a config array for a set of naming conventions to learn.
SvelteKitReact RouterVue Router
folder + +page.svelteroutes config / file-based (Next)routes array
+layout.svelte + {@render children()}<Outlet /><RouterView />
<a href> (auto-intercepted)<Link to><RouterLink to>
[id] folder + page.params:id + useParams():id + route.params
goto()useNavigate()router.push()

Coding Challenges

Challenge 1

Set up a SvelteKit app with three pages — Home (/), About (/about), Contact (/contact) — as +page.svelte files, plus a +layout.svelte with a nav (plain anchor tags) wrapping all of them via {@render children()}.

📄 View solution
Challenge 2

Add a dynamic product/[id] route. From a product list page, link to /product/[id] for each product with plain anchors, and in the [id] page read page.params.id (via $derived) to display the matching product.

📄 View solution
Challenge 3

Build a page with a button that uses goto() to navigate programmatically to another route (e.g. after a simulated action), confirming it does client-side navigation without a full reload.

📄 View solution

Chapter 11 Quick Reference

  • SvelteKit — Svelte's meta-framework (routing, SSR, data loading); npx sv create
  • File-based routing — folders under src/routes are URL segments; +page.svelte is the page
  • +layout.svelte + {@render children()} — shared UI across routes (= <Outlet> / <RouterView>)
  • Navigate with plain <a href> — auto-intercepted; no <Link>/<RouterLink> component
  • [id] folder — dynamic segment; read with page.params.id from $app/state (wrap in $derived)
  • goto(url) from $app/navigation — programmatic navigation
  • Other + files: +error.svelte, +page.js (next chapter); [...rest] is the catch-all
  • Next chapter: data loading and global state patterns (the final chapter)