S SvelteKurs
M8 · SvelteKit Routing

Dynamische Routen

Nicht jede URL ist statisch bekannt. Mit eckigen Klammern im Ordnernamen werden Teile der URL zu Parametern — genau wie /blog/:id in Rails, nur dateibasiert.

Ein Segment: [slug]

Ein Ordner mit eckigen Klammern wie [slug] macht dieses URL-Segment dynamisch. src/routes/blog/[slug]/+page.svelte bedient damit /blog/hallo-welt, /blog/svelte-tipps und jede andere einzelne Blog-URL — mit einer Datei.

Blog-Detailseite bash
src/routes/blog/[slug]/
├── +page.svelte      # zeigt einen Artikel
└── +page.ts          # lädt den Artikel anhand des slug

Den konkreten Wert bekommst du in der load-Funktion über params.slug — der Name entspricht exakt dem Klammer-Namen im Ordner.

src/routes/blog/[slug]/+page.ts ts
// src/routes/blog/[slug]/+page.ts
import { error } from '@sveltejs/kit';
import type { PageLoad } from './$types';

export const load: PageLoad = async ({ params, fetch }) => {
  // params.slug ist der dynamische Teil der URL
  const res = await fetch(`/api/posts/${params.slug}`);
  if (!res.ok) error(404, 'Artikel nicht gefunden');

  return { post: await res.json() };
};

In der Komponente stehen die geladenen Daten via data-Prop bereit; den rohen Parameter erreichst du zusätzlich reaktiv über page.params aus $app/state.

src/routes/blog/[slug]/+page.svelte svelte
<!-- src/routes/blog/[slug]/+page.svelte -->
<script lang="ts">
  import { page } from '$app/state';
  let { data } = $props();
</script>

<h1>{data.post.titel}</h1>
<p class="meta">slug: {page.params.slug}</p>
<article>{@html data.post.inhalt}</article>

🛤️ Rails-Vergleich: dynamische Segmente & Constraints

[slug] entspricht dem :id / :slug in Rails-Routen. Wo du in Rails auf params[:id] im Controller zugreifst, nutzt du in SvelteKit params.slug in load. Route-Constraints (constraints:) heißen hier Matcher.

config/routes.rb ruby
# config/routes.rb
resources :blog, only: :show          # /blog/:id
get "docs/*pfad", to: "docs#show"     # Wildcard/Glob

# Constraint = SvelteKit-Matcher
get "produkt/:id", to: "produkte#show",
    constraints: { id: /\d+/ }        # nur Ziffern

# Zugriff im Controller: params[:id], params[:slug]

Live: URL-Muster gegen Pfade

Wähle einen Beispiel-Pfad und sieh, welchen slug das Muster /blog/[slug] extrahiert:

▶ /blog/[slug] · Interaktiv

Pfad: /blog/hallo-welt

params.slug = "hallo-welt"

Rest, optional & Matcher

Neben dem einfachen [slug] gibt es zwei weitere Klammer-Formen:

  • Rest-Parameter [...pfad] fängt beliebig viele Segmente auf — gut für Dokumentations-Bäume wie /docs/a/b/c.
  • Optionaler Parameter [[lang]] matcht mit oder ohne das Segment — nützlich für Sprachpräfixe (/ und /de).
Klammer-Varianten bash
src/routes/
├── blog/[slug]/+page.svelte        # ein Segment:  /blog/hallo
├── docs/[...pfad]/+page.svelte     # Rest (beliebig tief):
│                                   #   /docs/a, /docs/a/b/c
└── [[lang]]/+page.svelte           # optional: / und /de, /en

Matcher: Parameter validieren

Standardmäßig matcht [slug] jeden Text. Willst du einschränken — z. B. nur numerische IDs — definierst du einen wiederverwendbaren Matcher in src/params/ und referenzierst ihn mit [id=integer].

src/params/integer.ts ts
// src/params/integer.ts — wiederverwendbarer Matcher
import type { ParamMatcher } from '@sveltejs/kit';

export const match: ParamMatcher = (param) => {
  return /^\d+$/.test(param);   // nur Ziffern
};
Matcher anwenden bash
src/routes/
└── produkt/[id=integer]/+page.svelte
    #   /produkt/42   → matcht  (params.id = "42")
    #   /produkt/abc  → matcht NICHT → 404 (oder andere Route)

⚠️ Matcher gehören zum Routing, nicht zur Validierung

Ein Matcher entscheidet nur, ob eine Route greift. Passt der Parameter nicht, versucht SvelteKit andere Routen und liefert sonst 404 — er ersetzt keine inhaltliche Prüfung deiner Daten in load.

Zusammenfassung

💡 Das Wichtigste

  • [slug] macht ein Segment dynamisch; Zugriff via params.slug.
  • [...pfad] fängt beliebig viele Segmente (Rest) auf.
  • [[lang]] ist optional — matcht mit und ohne das Segment.
  • [id=integer] bindet einen Matcher aus src/params/ ein.
  • In der Komponente reaktiv über page.params erreichbar.

🎯 Probier es selbst

Lege eine Route src/routes/blog/[slug]/+page.svelte mit passender +page.ts an, die params.slug ausliest und (zunächst) einfach als Überschrift ausgibt. Ergänze anschließend einen Matcher src/params/integer.ts und eine Route produkt/[id=integer] — teste, dass /produkt/42 matcht, /produkt/abc aber einen 404 liefert.