load — universal (+page.ts)
Daten laden, bevor die Seite gerendert wird. Die universelle load-Funktion läuft auf Server UND Client — ideal für öffentliche APIs.
Wozu eine load-Funktion?
Eine Komponente soll nicht selbst im onMount Daten nachladen — das führt zu leeren
Seiten, Flackern und schlechtem SEO. In SvelteKit legst du das Laden in eine load-Funktion. Sie läuft, bevor die Seite gerendert wird;
ihr Rückgabewert steht in der Komponente als data-Prop bereit.
Die universelle Variante lebt in einer Datei namens +page.ts (neben deiner +page.svelte). „Universell" heißt: Sie läuft beim ersten Aufruf auf
dem Server (für HTML + SEO) und danach bei clientseitiger Navigation direkt im Browser.
// src/routes/blog/+page.ts
import type { PageLoad } from './$types';
export const load: PageLoad = async ({ fetch }) => {
const res = await fetch('/api/posts');
const posts = await res.json();
return { posts }; // landet in der data-Prop
};Und so kommst du in der Komponente an die Daten — über die data-Prop:
<script lang="ts">
import type { PageData } from './$types';
let { data }: { data: PageData } = $props();
</script>
<h1>Blog</h1>
<ul>
{#each data.posts as post (post.id)}
<li>{post.title}</li>
{/each}
</ul>🛤️ Rails-Vergleich: load ≈ Controller-Action
Eine load-Funktion ist das SvelteKit-Gegenstück zu einer Controller-Action, die @variablen setzt. Was du in Rails @posts = Post.all nennst und im View benutzt, ist hier return { posts } und in der Komponente data.posts. Der
Unterschied: universelles load läuft auch im Browser weiter, nicht nur pro
Request.
# Rails: der Controller lädt Daten und legt sie in @-Variablen ab
class PostsController < ApplicationController
def index
@posts = Post.all # -> im View als @posts verfügbar
end
def show
@post = Post.find(params[:slug])
end
endDas eingebaute fetch
Beachte, dass fetch aus dem load-Argument kommt — nutze dieses und nicht das globale fetch. SvelteKits Version kann relative URLs auflösen, sendet
bei internen Aufrufen Cookies und Header mit und vermeidet auf dem Server einen echten
Netzwerk-Roundtrip zu eigenen +server.ts-Endpunkten.
💡 Immer das load-fetch verwenden
Schreib async ({ fetch }) => { ... } und benutze genau dieses fetch. So funktioniert dein Code identisch auf Server und Client.
Zugriff auf Routen-Parameter
Dynamische Segmente wie [slug] stehen im load-Argument unter params bereit — analog zu params[:slug] in Rails.
// src/routes/blog/[slug]/+page.ts
import type { PageLoad } from './$types';
export const load: PageLoad = async ({ params, fetch }) => {
// params.slug kommt aus dem Ordnernamen [slug]
const res = await fetch(`/api/posts/${params.slug}`);
return { post: await res.json() };
};Universal oder Server?
Die Faustregel für die Wahl der load-Datei:
+page.ts(universal) — wenn die Daten aus einer öffentlichen API kommen, kein Geheimnis nötig ist und du beim Navigieren keinen Server-Roundtrip brauchst. Kann auch nicht-serialisierbare Werte (z. B. Klassen-Instanzen, Funktionen) an die Seite geben.+page.server.ts(server-only) — sobald du direkt auf die Datenbank, Secrets, Umgebungsvariablen oderlocals(z. B. den eingeloggten User) zugreifst. Der Code landet nie im Browser-Bundle.
⚠️ Faustregel
Im Zweifel: Braucht der Code ein Geheimnis oder DB-Zugriff?
→ +page.server.ts. Sonst reicht +page.ts. Beide Dateien dürfen sogar
gleichzeitig existieren — die Ergebnisse werden zusammengeführt.
Zusammenfassung
💡 Das Wichtigste
+page.tsexportiertload— läuft auf Server und Client.- Der Rückgabewert landet als
data-Prop in der+page.svelte. - Immer das
fetchaus dem Argument verwenden;paramsliefert Routen-Segmente. - Secrets/DB → lieber
+page.server.ts(nächste Lektion).
🎯 Probier es selbst
Lege eine Route /lernen/daten/demo mit einer +page.ts an, die von https://jsonplaceholder.typicode.com/users lädt und die Namen in einer Liste
anzeigt. Nutze das eingebaute fetch und gib das Ergebnis als data.users zurück.