# 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?.error}

{form.error}

{/if}
{#if form?.success}

Todo added!

{/if}
``` ### 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}
Loading {navigating.to?.url.pathname}... {#if navigating.type === 'popstate'} (going {navigating.delta > 0 ? 'forward' : 'back'}) {/if}
{/if} {#if updated.current}
New version available!
{/if}

Current route: {page.route.id}

Query params: {JSON.stringify(Object.fromEntries(page.url.searchParams))}

{#if page.form?.success}

Operation completed successfully!

{/if} {#if hasError}
Error {page.status}: {page.error?.message}
{/if} ``` ### Server Module Exports Read assets and access request context from anywhere in server code. ```js /// file: src/lib/server/auth.js import { getRequestEvent } from '$app/server'; import { redirect, error } from '@sveltejs/kit'; export function requireAuth(minRole = 'user') { const { locals, url } = getRequestEvent(); if (!locals.user) { redirect(307, `/login?redirectTo=${url.pathname}`); } const roles = ['user', 'admin', 'superadmin']; const userRoleIndex = roles.indexOf(locals.user.role); const requiredRoleIndex = roles.indexOf(minRole); if (userRoleIndex < requiredRoleIndex) { error(403, 'Insufficient permissions'); } return locals.user; } export function getUser() { const { locals } = getRequestEvent(); return locals.user ?? null; } ``` ```js /// file: src/routes/api/download/+server.js import { read } from '$app/server'; import dataFile from '$lib/assets/large-dataset.json?url'; /** @type {import('./$types').RequestHandler} */ export async function GET() { // Read imported asset without bundling const response = read(dataFile); return new Response(response.body, { headers: { 'content-type': 'application/json', 'content-disposition': 'attachment; filename="dataset.json"' } }); } ``` ```js /// file: src/routes/admin/+page.server.js import { requireAuth } from '$lib/server/auth'; import * as db from '$lib/server/database'; export async function load() { const user = requireAuth('admin'); const stats = await db.getAdminStats(); return { stats }; } ``` ### Remote Functions Type-safe server functions callable from anywhere with automatic validation. ```js /// file: src/lib/data.remote.js import * as v from 'valibot'; import { query, form, command, prerender } from '$app/server'; import * as db from '$lib/server/database'; import { error, redirect } from '@sveltejs/kit'; // Query: read data from server export const searchProducts = query( v.object({ term: v.string(), category: v.optional(v.string()), limit: v.optional(v.pipe(v.number(), v.minValue(1), v.maxValue(100))) }), async ({ term, category, limit = 10 }) => { const products = await db.searchProducts({ term, category, limit }); return products; } ); // Form: handle form submissions export const createProduct = form( v.object({ name: v.pipe(v.string(), v.nonEmpty()), price: v.pipe(v.number(), v.minValue(0)), description: v.string(), categoryId: v.string() }), async ({ name, price, description, categoryId }, invalid) => { // Check for duplicates const existing = await db.getProductByName(name); if (existing) { invalid(invalid.name('Product name already exists')); } const product = await db.createProduct({ name, price, description, categoryId }); redirect(303, `/products/${product.id}`); } ); // Command: trigger server actions export const incrementViews = command( v.string(), // product ID async (productId) => { await db.incrementProductViews(productId); } ); // Prerender: static data at build time export const getCategories = prerender(async () => { const categories = await db.getCategories(); return categories; }); ``` ```svelte {#await results}

Searching...

{:then products} {:catch error}

{error.message}

{/await} ``` ```svelte

Create Product

``` ### Adapter Configuration Configure deployment adapters and build-time transformations for various platforms. ```js /// file: svelte.config.js import adapter from '@sveltejs/adapter-node'; import { vitePreprocess } from '@sveltejs/vite-plugin-svelte'; /** @type {import('@sveltejs/kit').Config} */ const config = { preprocess: vitePreprocess(), kit: { adapter: adapter({ out: 'build', precompress: true, envPrefix: 'APP_' }), alias: { '$components': 'src/lib/components', '$utils': 'src/lib/utils', '$types': 'src/lib/types' }, csp: { mode: 'auto', directives: { 'default-src': ['self'], 'script-src': ['self'], 'style-src': ['self', 'unsafe-inline'], 'img-src': ['self', 'data:', 'https:'], 'font-src': ['self'], 'connect-src': ['self', 'https://api.example.com'] } }, csrf: { trustedOrigins: ['https://checkout.stripe.com'] }, env: { publicPrefix: 'PUBLIC_', privatePrefix: 'PRIVATE_' }, paths: { base: process.env.BASE_PATH || '', assets: process.env.ASSETS_URL || '' }, prerender: { concurrency: 10, crawl: true, entries: ['*'], handleHttpError: ({ status, path, message }) => { if (status === 404) return; throw new Error(`${status} ${path}: ${message}`); } }, version: { name: process.env.GIT_COMMIT_SHA || Date.now().toString(), pollInterval: 60000 } } }; export default config; ``` ### Custom Adapter Implementation Create a custom adapter to deploy to any platform. ```js /// file: adapter/index.js import { writeFileSync, mkdirSync } from 'fs'; import { fileURLToPath } from 'url'; /** @param {import('./index').AdapterOptions} options */ export default function adapter(options = {}) { /** @type {import('@sveltejs/kit').Adapter} */ return { name: 'adapter-custom-platform', async adapt(builder) { const out = options.out || 'build'; // Clean and create output directory builder.rimraf(out); builder.mkdirp(out); // Write static assets const staticFiles = builder.writeClient(`${out}/client`); builder.log.info(`Wrote ${staticFiles.length} client files`); // Write prerendered pages const prerenderedFiles = builder.writePrerendered(`${out}/prerendered`); builder.log.info(`Wrote ${prerenderedFiles.length} prerendered files`); // Write server bundle const serverFiles = builder.writeServer(`${out}/server`); builder.log.info(`Wrote ${serverFiles.length} server files`); // Generate server manifest const manifest = builder.generateManifest({ relativePath: './server' }); // Create entry point builder.copy( fileURLToPath(new URL('./entry.js', import.meta.url)), `${out}/index.js`, { replace: { SERVER_DIR: './server', MANIFEST: manifest, ENV_PREFIX: JSON.stringify(options.envPrefix || '') } } ); // Write platform config writeFileSync( `${out}/platform.json`, JSON.stringify({ runtime: 'nodejs18', memory: options.memory || 1024, timeout: options.timeout || 30 }, null, 2) ); // Compress assets if (options.precompress !== false) { await builder.compress(`${out}/client`); builder.log.info('Compressed client assets'); } }, async emulate() { return { platform({ config, prerender }) { return { env: process.env, region: 'local' }; } }; }, supports: { read: () => true } }; } ``` ## Summary SvelteKit provides a comprehensive framework for building modern web applications with a clear separation between server and client code, filesystem-based routing, and powerful data loading mechanisms. The framework's core APIs—including page components, load functions, layouts, form actions, and hooks—work together to handle common patterns like authentication, data fetching, and form submission with minimal boilerplate. The `+page.svelte` files define your UI, while `+page.js` and `+page.server.js` files handle data loading with full control over whether code runs universally or server-only. Advanced features like remote functions enable type-safe server calls with automatic validation, while the navigation APIs provide fine-grained control over client-side routing and data invalidation. The adapter system makes SvelteKit deployment-agnostic, allowing the same codebase to run on serverless platforms, traditional servers, or as static sites. Combined with built-in TypeScript support, automatic type generation, progressive enhancement for forms, and reactive state management through Svelte 5 runes, SvelteKit offers a complete solution for building everything from simple websites to complex, full-stack applications with excellent developer experience and production-ready performance.