# SvelteKit
## Introduction
SvelteKit is a full-stack web application framework built on Svelte, designed to streamline the development of high-performance, modern web applications. It provides a filesystem-based routing system, server-side rendering (SSR), static site generation (prerendering), and client-side navigation out of the box. Similar to Next.js for React or Nuxt for Vue, SvelteKit handles the complex infrastructure of web development while following modern best practices and leveraging Vite for lightning-fast development with Hot Module Replacement (HMR).
The framework offers comprehensive solutions for common development challenges including build optimizations, offline support, configurable rendering strategies (SSR, CSR, or prerendering), and extensive tooling for forms, navigation, and state management. SvelteKit applications are adapter-based, meaning they can be deployed to virtually any platform—from traditional Node.js servers to serverless environments like Vercel, Netlify, Cloudflare Workers, and static hosting providers. With TypeScript support, automatic type generation, and a focus on web standards, SvelteKit enables developers to build everything from simple static sites to complex, data-driven applications with minimal configuration.
## API Reference
### Page Component (+page.svelte)
Defines a page in your application that renders both on the server (SSR) and in the browser (CSR) by default.
```svelte
{data.post.title}
By {data.post.author} on {data.post.date}
{@html data.post.content}
```
### Universal Load Function (+page.js)
Exports a `load` function that runs on both server and client to fetch and prepare data for pages.
```js
/// file: src/routes/products/[id]/+page.js
import { error } from '@sveltejs/kit';
/** @type {import('./$types').PageLoad} */
export async function load({ params, fetch, parent, depends, url, setHeaders }) {
// Fetch from external API
const response = await fetch(`https://api.example.com/products/${params.id}`);
if (!response.ok) {
error(response.status, 'Product not found');
}
const product = await response.json();
// Cache for 5 minutes
setHeaders({
'cache-control': 'public, max-age=300'
});
// Access parent layout data
const { categories } = await parent();
// Declare dependency for manual invalidation
depends('app:products');
return {
product,
category: categories.find(c => c.id === product.categoryId),
relatedProducts: product.relatedIds.slice(0, 3)
};
}
// Page configuration exports
export const prerender = false;
export const ssr = true;
export const csr = true;
```
### Server Load Function (+page.server.js)
Exports a server-only `load` function with access to databases, private APIs, and sensitive environment variables.
```js
/// file: src/routes/dashboard/+page.server.js
import * as db from '$lib/server/database';
import { redirect, error } from '@sveltejs/kit';
import { env } from '$env/dynamic/private';
/** @type {import('./$types').PageServerLoad} */
export async function load({ cookies, locals, url, params, request, getClientAddress }) {
// Require authentication
const sessionId = cookies.get('sessionid');
if (!sessionId) {
redirect(307, `/login?redirectTo=${url.pathname}`);
}
// Verify session and get user
const user = await db.getUserFromSession(sessionId);
if (!user) {
cookies.delete('sessionid', { path: '/' });
redirect(307, '/login');
}
// Fetch user data from database
const [stats, activity, notifications] = await Promise.all([
db.getUserStats(user.id),
db.getRecentActivity(user.id, 10),
db.getUnreadNotifications(user.id)
]);
// Update last visit timestamp
cookies.set('last_visit', new Date().toISOString(), {
path: '/',
maxAge: 60 * 60 * 24 * 365,
httpOnly: true,
secure: true,
sameSite: 'lax'
});
return {
user: {
id: user.id,
name: user.name,
email: user.email,
avatar: user.avatar
},
stats,
activity,
notifications,
apiKey: env.INTERNAL_API_KEY // Private env vars only on server
};
}
```
### Layout Component (+layout.svelte)
Creates reusable layouts that wrap pages and persist across navigation, maintaining state.
```svelte
{@render children()}
```
### Layout Load Function (+layout.server.js)
Provides data to layouts and all child pages, useful for shared navigation data or authentication.
```js
/// file: src/routes/(app)/+layout.server.js
import * as db from '$lib/server/database';
/** @type {import('./$types').LayoutServerLoad} */
export async function load({ cookies, locals, depends }) {
// Declare dependency for invalidation
depends('app:navigation');
// Get authenticated user from locals (set by hooks)
const user = locals.user;
if (user) {
// Fetch navigation items and notifications
const [menuItems, unreadCount] = await Promise.all([
db.getMenuItemsForUser(user.id),
db.getUnreadNotificationCount(user.id)
]);
return {
user,
menuItems,
unreadCount
};
}
return {
user: null,
menuItems: [],
unreadCount: 0
};
}
```
### Error Page (+error.svelte)
Custom error boundary that displays when errors occur during load or rendering.
```svelte
{page.status}
{#if page.status === 404}
The page you're looking for doesn't exist.
{:else if page.status === 500}
Something went wrong on our end. We're working to fix it.
{page.error?.message}
{:else}
{page.error?.message || 'An error occurred'}
{/if}
```
### API Route (+server.js)
Creates server-side API endpoints by exporting HTTP verb functions.
```js
/// file: src/routes/api/posts/+server.js
import * as db from '$lib/server/database';
import { json, error } from '@sveltejs/kit';
/** @type {import('./$types').RequestHandler} */
export async function GET({ url, setHeaders }) {
const limit = Number(url.searchParams.get('limit') || '10');
const offset = Number(url.searchParams.get('offset') || '0');
if (limit > 100) {
error(400, 'Limit cannot exceed 100');
}
const posts = await db.getPosts({ limit, offset });
const total = await db.getPostCount();
setHeaders({
'cache-control': 'public, max-age=60',
'x-total-count': total.toString()
});
return json({
posts,
pagination: {
total,
limit,
offset,
hasMore: offset + limit < total
}
});
}
/** @type {import('./$types').RequestHandler} */
export async function POST({ request, locals, cookies }) {
if (!locals.user) {
error(401, 'Authentication required');
}
const data = await request.json();
// Validate input
if (!data.title || !data.content) {
error(400, 'Title and content are required');
}
// Create post
const post = await db.createPost({
title: data.title,
content: data.content,
authorId: locals.user.id
});
return json(post, { status: 201 });
}
/** @type {import('./$types').RequestHandler} */
export function fallback({ request }) {
return new Response(`Method ${request.method} not allowed`, {
status: 405,
headers: { 'allow': 'GET, POST' }
});
}
```
### Form Actions
Server-side form handling with progressive enhancement and validation.
```js
/// file: src/routes/todos/+page.server.js
import * as db from '$lib/server/database';
import { fail, redirect } from '@sveltejs/kit';
/** @type {import('./$types').PageServerLoad} */
export async function load({ locals }) {
if (!locals.user) {
redirect(307, '/login');
}
const todos = await db.getTodos(locals.user.id);
return { todos };
}
/** @satisfies {import('./$types').Actions} */
export const actions = {
create: async ({ request, locals }) => {
const data = await request.formData();
const description = data.get('description');
// Validation
if (!description || description.length < 3) {
return fail(400, {
description,
error: 'Description must be at least 3 characters',
missing: !description
});
}
try {
await db.createTodo({
userId: locals.user.id,
description: description.trim(),
completed: false
});
return { success: true };
} catch (err) {
return fail(500, { description, error: 'Failed to create todo' });
}
},
toggle: async ({ request, locals }) => {
const data = await request.formData();
const id = data.get('id');
await db.toggleTodo(locals.user.id, id);
return { toggled: id };
},
delete: async ({ request, locals }) => {
const data = await request.formData();
const id = data.get('id');
await db.deleteTodo(locals.user.id, id);
return { deleted: id };
}
};
```
```svelte
My Todos
{#if form?.success}
Todo added!
{/if}
{#each data.todos as todo}
{todo.description}
{/each}
```
### Server Hooks (handle)
Intercepts every server request to add authentication, logging, or custom routing logic.
```js
/// file: src/hooks.server.js
import { sequence } from '@sveltejs/kit/hooks';
import * as db from '$lib/server/database';
/** @type {import('@sveltejs/kit').Handle} */
async function authentication({ event, resolve }) {
const sessionId = event.cookies.get('sessionid');
if (sessionId) {
try {
event.locals.user = await db.getUserFromSession(sessionId);
} catch (err) {
console.error('Session lookup failed:', err);
event.cookies.delete('sessionid', { path: '/' });
}
}
return resolve(event);
}
/** @type {import('@sveltejs/kit').Handle} */
async function logging({ event, resolve }) {
const start = Date.now();
const response = await resolve(event);
const duration = Date.now() - start;
console.log(
`${event.request.method} ${event.url.pathname} - ${response.status} (${duration}ms)`
);
return response;
}
/** @type {import('@sveltejs/kit').Handle} */
async function customHeaders({ event, resolve }) {
const response = await resolve(event, {
transformPageChunk: ({ html }) => {
return html.replace('%BUILD_TIME%', new Date().toISOString());
},
filterSerializedResponseHeaders: (name) => {
return name.startsWith('x-custom-');
}
});
response.headers.set('x-powered-by', 'SvelteKit');
return response;
}
// Chain multiple hooks
export const handle = sequence(authentication, logging, customHeaders);
/** @type {import('@sveltejs/kit').HandleFetch} */
export async function handleFetch({ event, request, fetch }) {
// Rewrite API calls during SSR
if (request.url.startsWith('https://api.example.com/')) {
request = new Request(
request.url.replace('https://api.example.com/', 'http://localhost:9999/'),
request
);
}
// Forward auth headers
if (request.url.startsWith(event.url.origin)) {
request.headers.set('cookie', event.request.headers.get('cookie') ?? '');
}
return fetch(request);
}
/** @type {import('@sveltejs/kit').HandleServerError} */
export async function handleError({ error, event, status, message }) {
const errorId = crypto.randomUUID();
// Log to external service
console.error(`[${errorId}] ${status}:`, error, {
url: event.url.pathname,
user: event.locals.user?.id
});
return {
message: 'An unexpected error occurred',
errorId
};
}
```
### Navigation Functions
Programmatic navigation and data invalidation from client-side code.
```svelte
```
### Reactive State ($app/state)
Access current page state, navigation status, and app version updates.
```svelte
{#if navigating}