Page Data and Typing
How data flows from load to a component's props, and using generated types to keep the two in sync.
阅读需 2 分钟
Every load function's return value ends up as a single data prop on the matching component — but as an app grows, keeping the shape of that data in sync between +page.server.js and +page.svelte by hand becomes error-prone. SvelteKit generates types for exactly this, so a typo or a renamed field is caught before you ever run the app.
The data flow, end to end
// src/routes/products/[id]/+page.server.js
export async function load({ params }) {
return {
product: {
id: params.id,
name: 'Wireless Mouse',
price: 29.99
}
};
}<!-- src/routes/products/[id]/+page.svelte -->
<script>
let { data } = $props();
</script>
<h1>{data.product.name}</h1>
<p>${data.product.price}</p>There's no import connecting these two files — the link is purely positional, based on both living in products/[id]/. That implicit contract is exactly what generated types make explicit and checkable.
Generated types with JSDoc or TypeScript
In a TypeScript project, SvelteKit generates a ./$types module per route, containing a PageServerLoad type for your load function and a PageProps (or PageData) type for your component's props:
// src/routes/products/[id]/+page.server.ts
import type { PageServerLoad } from './$types';
export const load: PageServerLoad = async ({ params }) => {
return {
product: { id: params.id, name: 'Wireless Mouse', price: 29.99 }
};
};<!-- src/routes/products/[id]/+page.svelte -->
<script lang="ts">
import type { PageProps } from './$types';
let { data }: PageProps = $props();
</script>
<h1>{data.product.name}</h1>PageProps is inferred from your own load function's return type — rename product to item in +page.server.ts and the component's data.product reference becomes a type error immediately, without writing a single interface by hand. The types live in a generated .svelte-kit directory and update automatically as you edit routes.
Combining data from layout and page
When both a +layout.server.js and a +page.server.js return data, the page's component sees them merged into one data object:
// +layout.server.js
export async function load() {
return { siteName: 'My Shop' };
}// +page.server.js
export async function load() {
return { product: { name: 'Wireless Mouse' } };
}<script>
let { data } = $props();
// data.siteName AND data.product are both available
</script>If a layout and a page return a key with the same name, the page's value wins — it's merged shallowly, layout-to-page, root to leaf.
Why this matters more than it looks
It's tempting to treat data as just another prop and move on, but the load-to-component link is the seam where most real bugs happen: a field renamed on the server, a query that starts returning null instead of an empty array, an API response shape that changes. Leaning on generated types turns those into build-time errors in your editor, rather than a blank product page a user finds in production.