## docs/kit/10-getting-started/10-introduction.md # Introduction ## What is SvelteKit? SvelteKit is a framework for rapidly developing robust, performant web applications using Svelte. Similar to Next.js (React) or Nuxt (Vue). ## What is Svelte? Svelte is a way of writing UI components that compiles to JavaScript and CSS. The compiler converts components to optimized code that renders HTML and styles. ## SvelteKit vs Svelte - **Svelte**: Renders UI components - **SvelteKit**: Full-stack framework with routing, build optimizations, offline support, preloading, configurable rendering (SSR/CSR/prerendering), image optimization, and more SvelteKit provides modern best practices and leverages Vite with Hot Module Replacement for fast development. ## docs/kit/10-getting-started/20-creating-a-project.md # Creating a project Start building a SvelteKit app: ```bash npx sv create my-app cd my-app npm install npm run dev ``` This scaffolds a project in `my-app` directory with optional TypeScript setup, installs dependencies, and starts server on [localhost:5173](http://localhost:5173). ## Key concepts - Each page is a Svelte component - Create pages by adding files to `src/routes` directory - Pages are server-rendered first, then client-side app takes over ## Editor setup Use [Visual Studio Code](https://code.visualstudio.com/download) with [the Svelte extension](https://marketplace.visualstudio.com/items?itemName=svelte.svelte-vscode). ## docs/kit/10-getting-started/25-project-types.md # Project Types SvelteKit offers configurable rendering for different deployment scenarios. Rendering settings are not mutually exclusive - you can mix different approaches within the same app. ## Default Rendering First page uses SSR, subsequent pages use CSR. This hybrid approach improves SEO and perceived performance while maintaining fast navigation. ## Static Site Generation Use `adapter-static` to prerender your entire site, or use the `prerender` option to prerender specific pages: ```js // +page.js export const prerender = true; ``` For large sites, use Incremental Static Regeneration with `adapter-vercel`. ## Single-Page App Build SPAs using CSR exclusively. Skip server-related docs if using no backend or separate backend. ## Multi-Page App Remove JavaScript with `csr = false` or use `data-sveltekit-reload` for server-rendered links: ```js // +page.js export const csr = false; ``` ## Separate Backend Deploy frontend separately using `adapter-node` or serverless adapters. Skip server file documentation. ## Deployment Options - **Serverless**: Use `adapter-auto`, `adapter-vercel`, `adapter-netlify`, or `adapter-cloudflare` - **Own Server**: Use `adapter-node` - **Container**: Use `adapter-node` with Docker/LXC - **Library**: Use `@sveltejs/package` with `sv create` ## Specialized Apps - **Offline/PWA**: Full service worker support - **Mobile**: Convert SPA using Tauri or Capacitor - **Desktop**: Convert SPA using Tauri, Wails, or Electron - **Browser Extension**: Use `adapter-static` or specialized adapters - **Embedded**: Use `bundleStrategy: 'single'` to reduce concurrent requests ```js // svelte.config.js export default { kit: { output: { bundleStrategy: 'single' } } }; ``` ## docs/kit/10-getting-started/30-project-structure.md # Project structure ## Basic structure ```bash my-project/ ├ src/ │ ├ lib/ │ │ ├ server/ │ │ │ └ [server-only lib files] │ │ └ [lib files] │ ├ params/ │ │ └ [param matchers] │ ├ routes/ │ │ └ [routes] │ ├ app.html │ ├ error.html │ ├ hooks.client.js │ ├ hooks.server.js │ └ service-worker.js ├ static/ │ └ [static assets] ├ package.json ├ svelte.config.js └ vite.config.js ``` ## Key directories ### src/ - `lib` - Library code, imported via `$lib` alias - `lib/server` - Server-only code, imported via `$lib/server` alias - `params` - Param matchers for advanced routing - `routes` - Application routes - `app.html` - Page template with placeholders: - `%sveltekit.head%` - Links, scripts, `` content - `%sveltekit.body%` - Rendered page markup (wrap in `
`, not directly in ``) - `%sveltekit.assets%` - Asset path - `%sveltekit.nonce%` - CSP nonce - `%sveltekit.env.[NAME]%` - Environment variables (must start with `publicPrefix`) - `error.html` - Error page with `%sveltekit.status%` and `%sveltekit.error.message%` - `hooks.client.js` - Client hooks - `hooks.server.js` - Server hooks - `service-worker.js` - Service worker ### static/ Static assets served as-is (robots.txt, favicon.png, etc.) ## Config files ### package.json Must include `@sveltejs/kit`, `svelte`, `vite` as `devDependencies`. Requires `"type": "module"`. ### svelte.config.js Svelte and SvelteKit configuration. ### vite.config.js Vite project using `@sveltejs/kit/vite` plugin. ### .svelte-kit Generated files (can be deleted, will regenerate). ## docs/kit/10-getting-started/40-web-standards.md # Web Standards SvelteKit builds on standard [Web APIs](https://developer.mozilla.org/en-US/docs/Web/API) rather than reinventing them. These APIs work in all modern browsers and many non-browser environments. ## Fetch APIs SvelteKit uses [`fetch`](https://developer.mozilla.org/en-US/docs/Web/API/fetch) for network requests. Available in hooks, server routes, and browsers. > **Note:** Special version available in `load` functions, server hooks, and API routes for direct endpoint invocation during SSR without HTTP calls, preserving credentials. ### Request [`Request`](https://developer.mozilla.org/en-US/docs/Web/API/Request) accessible as `event.request` in hooks and server routes. Use `request.json()` and `request.formData()` for posted data. ### Response [`Response`](https://developer.mozilla.org/en-US/docs/Web/API/Response) returned from `await fetch(...)` and `+server.js` handlers. ### Headers [`Headers`](https://developer.mozilla.org/en-US/docs/Web/API/Headers) interface for reading `request.headers` and setting `response.headers`: ```js // @errors: 2461 /// file: src/routes/what-is-my-user-agent/+server.js import { json } from '@sveltejs/kit'; /** @type {import('./$types').RequestHandler} */ export function GET({ request }) { // log all headers console.log(...request.headers); // create a JSON Response using a header we received return json({ // retrieve a specific header userAgent: request.headers.get('user-agent') }, { // set a header on the response headers: { 'x-custom-header': 'potato' } }); } ``` ## FormData For HTML form submissions, use [`FormData`](https://developer.mozilla.org/en-US/docs/Web/API/FormData): ```js // @errors: 2461 /// file: src/routes/hello/+server.js import { json } from '@sveltejs/kit'; /** @type {import('./$types').RequestHandler} */ export async function POST(event) { const body = await event.request.formData(); // log all fields console.log([...body]); return json({ // get a specific field's value name: body.get('name') ?? 'world' }); } ``` ## Stream APIs For large or chunked responses, use [streams](https://developer.mozilla.org/en-US/docs/Web/API/Streams_API): [ReadableStream](https://developer.mozilla.org/en-US/docs/Web/API/ReadableStream), [WritableStream](https://developer.mozilla.org/en-US/docs/Web/API/WritableStream), [TransformStream](https://developer.mozilla.org/en-US/docs/Web/API/TransformStream). ## URL APIs [`URL`](https://developer.mozilla.org/en-US/docs/Web/API/URL) interface with `origin`, `pathname` properties. Found in `event.url`, `page.url`, navigation functions. ### URLSearchParams Access query parameters via `url.searchParams` ([`URLSearchParams`](https://developer.mozilla.org/en-US/docs/Web/API/URLSearchParams)): ```js const foo = url.searchParams.get('foo'); ``` ## Web Crypto [Web Crypto API](https://developer.mozilla.org/en-US/docs/Web/API/Web_Crypto_API) via `crypto` global. Used for CSP headers and UUID generation: ```js const uuid = crypto.randomUUID(); ``` ## docs/kit/20-core-concepts/10-routing.md # Routing SvelteKit uses filesystem-based routing where directories define URL paths: - `src/routes` → root route - `src/routes/about` → `/about` route - `src/routes/blog/[slug]` → route with parameter `slug` Route files are identified by `+` prefix. Key rules: * All files run on server * All files run on client except `+server` files * `+layout` and `+error` files apply to subdirectories ## +page ### +page.svelte Defines a page component. Rendered on server (SSR) initially, then client (CSR) for navigation. ```svelte

Hello and welcome to my site!

About my site ``` Pages receive data via `data` prop: ```svelte

{data.title}

{@html data.content}
``` ### +page.js Exports `load` function to fetch data. Runs on server and client. ```js /// file: src/routes/blog/[slug]/+page.js import { error } from '@sveltejs/kit'; /** @type {import('./$types').PageLoad} */ export function load({ params }) { if (params.slug === 'hello-world') { return { title: 'Hello world!', content: 'Welcome to our blog. Lorem ipsum dolor sit amet...' }; } error(404, 'Not found'); } ``` Can export page options: `prerender`, `ssr`, `csr`. ### +page.server.js Server-only `load` function for database access or private environment variables. ```js /// file: src/routes/blog/[slug]/+page.server.js import { error } from '@sveltejs/kit'; /** @type {import('./$types').PageServerLoad} */ export async function load({ params }) { const post = await getPostFromDatabase(params.slug); if (post) { return post; } error(404, 'Not found'); } ``` Return value must be serializable. Can also export actions for form handling. ## +error Customizes error pages. SvelteKit walks up tree to find closest error boundary. ```svelte

{page.status}: {page.error.message}

``` For 404s, uses `src/routes/+error.svelte`. Fallback is `src/error.html`. ## +layout ### +layout.svelte Wraps pages with shared UI. Must include `{@render children()}`. ```svelte {@render children()} ``` Layouts can be nested and inherit from parent layouts. ### +layout.js Provides data to layout and all child pages: ```js /// file: src/routes/settings/+layout.js /** @type {import('./$types').LayoutLoad} */ export function load() { return { sections: [ { slug: 'profile', title: 'Profile' }, { slug: 'notifications', title: 'Notifications' } ] }; } ``` ### +layout.server.js Server-only layout load function. Change `LayoutLoad` to `LayoutServerLoad`. ## +server API routes with HTTP verb handlers. Return `Response` objects. ```js /// file: src/routes/api/random-number/+server.js import { error } from '@sveltejs/kit'; /** @type {import('./$types').RequestHandler} */ export function GET({ url }) { const min = Number(url.searchParams.get('min') ?? '0'); const max = Number(url.searchParams.get('max') ?? '1'); const d = max - min; if (isNaN(d) || d < 0) { error(400, 'min and max must be numbers, and min must be less than max'); } const random = min + Math.random() * d; return new Response(String(random)); } ``` ### Receiving data Handle POST/PUT/PATCH/DELETE requests: ```js /// file: src/routes/api/add/+server.js import { json } from '@sveltejs/kit'; /** @type {import('./$types').RequestHandler} */ export async function POST({ request }) { const { a, b } = await request.json(); return json(a + b); } ``` ### Fallback handler Handles unmatched HTTP methods: ```js /** @type {import('./$types').RequestHandler} */ export async function fallback({ request }) { return text(`I caught your ${request.method} request!`); } ``` ### Content negotiation Same directory can have `+page` and `+server`: - `PUT`/`PATCH`/`DELETE`/`OPTIONS` → always `+server.js` - `GET`/`POST`/`HEAD` → `+page` if `accept` header prioritizes `text/html`, else `+server.js` ## $types SvelteKit generates TypeScript types in `$types.d.ts`: ```svelte ``` Available types: - `PageProps`, `LayoutProps` (components) - `PageLoad`, `PageServerLoad` (page load functions) - `LayoutLoad`, `LayoutServerLoad` (layout load functions) - `RequestHandler` (server routes) IDE tooling can auto-insert these types. ## Other files Non-route files in route directories are ignored. Use `$lib` for shared components. ## docs/kit/20-core-concepts/20-load.md # Loading data Before `+page.svelte` and `+layout.svelte` components render, get data using `load` functions. ## Page data `+page.js` exports a `load` function, return value available via `data` prop: ```js /// file: src/routes/blog/[slug]/+page.js /** @type {import('./$types').PageLoad} */ export function load({ params }) { return { post: { title: `Title for ${params.slug} goes here`, content: `Content for ${params.slug} goes here` } }; } ``` ```svelte

{data.post.title}

{@html data.post.content}
``` `+page.js` runs on server and browser. For server-only (database access, private env vars), use `+page.server.js`: ```js /// file: src/routes/blog/[slug]/+page.server.js import * as db from '$lib/server/database'; /** @type {import('./$types').PageServerLoad} */ export async function load({ params }) { return { post: await db.getPost(params.slug) }; } ``` ## Layout data `+layout.js` or `+layout.server.js` load data for layouts: ```js /// file: src/routes/blog/[slug]/+layout.server.js import * as db from '$lib/server/database'; /** @type {import('./$types').LayoutServerLoad} */ export async function load() { return { posts: await db.getPostSummaries() }; } ``` ```svelte
{@render children()}
``` Layout data is available to child layouts and pages. If multiple `load` functions return same key, last one wins. ## page.data Parent layouts can access page data via `page.data`: ```svelte {page.data.title} ``` ## Universal vs server **Universal** (`+page.js`, `+layout.js`): Run on server during SSR, then in browser **Server** (`+page.server.js`, `+layout.server.js`): Server-only Server `load` runs first if both exist. Universal gets server data via `data` property: ```js /// file: src/routes/+page.server.js /** @type {import('./$types').PageServerLoad} */ export async function load() { return { serverMessage: 'hello from server load function' }; } ``` ```js /// file: src/routes/+page.js /** @type {import('./$types').PageLoad} */ export async function load({ data }) { return { serverMessage: data.serverMessage, universalMessage: 'hello from universal load function' }; } ``` **Use server** for: database access, private credentials **Use universal** for: external APIs, non-serializable data ## URL data Access `url`, `route`, `params`: ```js /// file: src/routes/a/[b]/[...c]/+page.js /** @type {import('./$types').PageLoad} */ export function load({ route, url, params }) { console.log(route.id); // '/a/[b]/[...c]' // url.pathname: '/a/x/y/z' → params: { "b": "x", "c": "y/z" } } ``` ## Making fetch requests Use provided `fetch` function: ```js /// file: src/routes/items/[id]/+page.js /** @type {import('./$types').PageLoad} */ export async function load({ fetch, params }) { const res = await fetch(`/api/items/${params.id}`); const item = await res.json(); return { item }; } ``` Benefits: inherits cookies/auth, relative URLs work, internal requests optimized, responses inlined during SSR. ## Cookies Server `load` functions can access cookies: ```js /// file: src/routes/+layout.server.js import * as db from '$lib/server/database'; /** @type {import('./$types').LayoutServerLoad} */ export async function load({ cookies }) { const sessionid = cookies.get('sessionid'); return { user: await db.getUser(sessionid) }; } ``` ## Headers Set response headers with `setHeaders`: ```js /// file: src/routes/products/+page.js /** @type {import('./$types').PageLoad} */ export async function load({ fetch, setHeaders }) { const response = await fetch('https://cms.example.com/products.json'); setHeaders({ age: response.headers.get('age'), 'cache-control': response.headers.get('cache-control') }); return response.json(); } ``` ## Using parent data Access parent `load` data with `await parent()`: ```js /// file: src/routes/+layout.js /** @type {import('./$types').LayoutLoad} */ export function load() { return { a: 1 }; } ``` ```js /// file: src/routes/abc/+page.js /** @type {import('./$types').PageLoad} */ export async function load({ parent }) { const { a } = await parent(); return { c: a + 1 }; } ``` Avoid waterfalls - call independent functions first: ```js /// file: +page.js /** @type {import('./$types').PageLoad} */ export async function load({ params, parent }) { const data = await getData(params); const parentData = await parent(); return { ...data, meta: { ...parentData.meta, ...data.meta } }; } ``` ## Errors Use `error` helper for expected errors: ```js /// file: src/routes/admin/+layout.server.js import { error } from '@sveltejs/kit'; /** @type {import('./$types').LayoutServerLoad} */ export function load({ locals }) { if (!locals.user) { error(401, 'not logged in'); } if (!locals.user.isAdmin) { error(403, 'not an admin'); } } ``` ## Redirects Use `redirect` helper: ```js /// file: src/routes/user/+layout.server.js import { redirect } from '@sveltejs/kit'; /** @type {import('./$types').LayoutServerLoad} */ export function load({ locals }) { if (!locals.user) { redirect(307, '/login'); } } ``` ## Streaming with promises Server `load` can return promises for streaming: ```js /// file: src/routes/blog/[slug]/+page.server.js /** @type {import('./$types').PageServerLoad} */ export async function load({ params }) { return { comments: loadComments(params.slug), // streamed post: await loadPost(params.slug) // awaited }; } ``` ```svelte

{data.post.title}

{@html data.post.content}
{#await data.comments} Loading comments... {:then comments} {#each comments as comment}

{comment.content}

{/each} {:catch error}

error loading comments: {error.message}

{/await} ``` ## Rerunning load functions `load` functions rerun when: - Referenced `params` change - Referenced `url` properties change - `await parent()` and parent reruns - Dependency marked invalid with `invalidate(url)` - `invalidateAll()` called ### Manual invalidation ```js /// file: src/routes/random-number/+page.js /** @type {import('./$types').PageLoad} */ export async function load({ fetch, depends }) { const response = await fetch('https://api.example.com/random-number'); depends('app:random'); return { number: await response.json() }; } ``` ```svelte

random number: {data.number}

``` ### Untracking dependencies Exclude from dependency tracking with `untrack`: ```js /// file: src/routes/+page.js /** @type {import('./$types').PageLoad} */ export async function load({ untrack, url }) { if (untrack(() => url.pathname === '/')) { return { message: 'Welcome!' }; } } ``` ## Using getRequestEvent Access request event in shared logic: ```js /// file: src/lib/server/auth.js import { redirect } from '@sveltejs/kit'; import { getRequestEvent } from '$app/server'; export function requireLogin() { const { locals, url } = getRequestEvent(); if (!locals.user) { const redirectTo = url.pathname + url.search; const params = new URLSearchParams({ redirectTo }); redirect(307, `/login?${params}`); } return locals.user; } ``` ```js /// file: +page.server.js import { requireLogin } from '$lib/server/auth'; export function load() { const user = requireLogin(); return { message: `hello ${user.name}!` }; } ``` ## docs/kit/20-core-concepts/30-form-actions.md # Form actions A `+page.server.js` file can export _actions_ for `POST` data using `
` elements. Client-side JavaScript is optional but enables progressive enhancement. ## Default actions ```js /// file: src/routes/login/+page.server.js /** @satisfies {import('./$types').Actions} */ export const actions = { default: async (event) => { // TODO log the user in } }; ``` ```svelte
``` Actions always use `POST` requests. Invoke from other pages with `action` attribute: ```html /// file: src/routes/+layout.svelte
``` ## Named actions ```js /// file: src/routes/login/+page.server.js /** @satisfies {import('./$types').Actions} */ export const actions = { login: async (event) => { // TODO log the user in }, register: async (event) => { // TODO register the user } }; ``` Invoke with query parameter prefixed by `/`: ```svelte
``` Use `formaction` on buttons: ```svelte /// file: src/routes/login/+page.svelte
``` > Can't have default actions next to named actions due to query parameter persistence. ## Anatomy of an action Actions receive `RequestEvent` and can return data available through `form` prop: ```js /// file: src/routes/login/+page.server.js import * as db from '$lib/server/db'; /** @satisfies {import('./$types').Actions} */ export const actions = { login: async ({ cookies, request }) => { const data = await request.formData(); const email = data.get('email'); const password = data.get('password'); const user = await db.getUser(email); cookies.set('sessionid', await db.createSession(user), { path: '/' }); return { success: true }; } }; ``` ```svelte {#if form?.success}

Successfully logged in! Welcome back, {data.user.name}

{/if} ``` ### Validation errors Use `fail()` to return validation errors with HTTP status codes: ```js /// file: src/routes/login/+page.server.js import { fail } from '@sveltejs/kit'; import * as db from '$lib/server/db'; /** @satisfies {import('./$types').Actions} */ export const actions = { login: async ({ cookies, request }) => { const data = await request.formData(); const email = data.get('email'); const password = data.get('password'); if (!email) { return fail(400, { email, missing: true }); } const user = await db.getUser(email); if (!user || user.password !== db.hash(password)) { return fail(400, { email, incorrect: true }); } cookies.set('sessionid', await db.createSession(user), { path: '/' }); return { success: true }; } }; ``` ```svelte /// file: src/routes/login/+page.svelte
{#if form?.missing}

The email field is required

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

Invalid credentials!

{/if}
``` ### Redirects ```js /// file: src/routes/login/+page.server.js import { fail, redirect } from '@sveltejs/kit'; /** @satisfies {import('./$types').Actions} */ export const actions = { login: async ({ cookies, request, url }) => { // ... validation logic cookies.set('sessionid', await db.createSession(user), { path: '/' }); if (url.searchParams.has('redirectTo')) { redirect(303, url.searchParams.get('redirectTo')); } return { success: true }; } }; ``` ## Progressive enhancement ### use:enhance Add `use:enhance` for progressive enhancement: ```svelte /// file: src/routes/login/+page.svelte
``` > Only works with `method="POST"` forms pointing to `+page.server.js` actions. Without arguments, `use:enhance` emulates browser behavior without full-page reloads. ### Customising use:enhance ```svelte { // `formElement` is this `` element // `formData` is its `FormData` object that's about to be submitted // `action` is the URL to which the form is posted // calling `cancel()` will prevent the submission // `submitter` is the `HTMLElement` that caused the form to be submitted return async ({ result, update }) => { // `result` is an `ActionResult` object // `update` is a function which triggers the default logic that would be triggered if this callback wasn't set }; }} > ``` Use `applyAction` to reproduce default behavior: ```svelte /// file: src/routes/login/+page.svelte { return async ({ result }) => { if (result.type === 'redirect') { goto(result.location); } else { await applyAction(result); } }; }} > ``` ### Custom event listener ```svelte
``` Use `deserialize()` instead of `JSON.parse()`. For `+server.js` alongside `+page.server.js`, add header: ```js const response = await fetch(this.action, { method: 'POST', body: data, headers: { 'x-sveltekit-action': 'true' } }); ``` ## GET vs POST Use `method="GET"` for forms that don't need to POST data (like search): ```html
``` This navigates to `/search?q=...` and invokes load functions but not actions. Supports `data-sveltekit-*` attributes like `` elements. ## docs/kit/20-core-concepts/40-page-options.md # Page Options Control rendering behavior by exporting options from `+page.js`, `+page.server.js`, `+layout.js`, or `+layout.server.js`. Child layouts/pages override parent values. ## prerender Generate static HTML at build time: ```js /// file: +page.js/+page.server.js/+server.js export const prerender = true; ``` Set in root layout to prerender everything, then opt-out specific pages: ```js /// file: +page.js/+page.server.js/+server.js export const prerender = false; ``` Include in manifest for dynamic SSR: ```js /// file: +page.js/+page.server.js/+server.js export const prerender = 'auto'; ``` **Requirements:** - All users must get same content from server - No personalized data (fetch in `onMount` instead) - No `url.searchParams` access during prerendering - No form actions **Server routes:** Inherit prerender setting from pages that fetch from them. **Conflicts:** Avoid same-name files/directories. Use extensions: `foo.json/+server.js` not `foo/+server.js`. ## entries Define dynamic routes to prerender when not discoverable by crawling: ```js /// file: src/routes/blog/[slug]/+page.server.js /** @type {import('./$types').EntryGenerator} */ export function entries() { return [ { slug: 'hello-world' }, { slug: 'another-blog-post' } ]; } export const prerender = true; ``` ## ssr Disable server-side rendering (renders empty shell): ```js /// file: +page.js export const ssr = false; // If both `ssr` and `csr` are `false`, nothing renders! ``` ## csr Disable client-side JavaScript: ```js /// file: +page.js export const csr = false; ``` **Effects:** - No JavaScript shipped to client - ` ``` ```svelte

Welcome {user().name}

``` > Pass functions to `setContext` to maintain reactivity. State updates during SSR won't affect parent components already rendered. ## Component state is preserved Components are reused during navigation. Make values reactive: ```svelte /// file: src/routes/blog/[slug]/+page.svelte ``` To force component recreation: ```svelte {#key page.url.pathname} {/key} ``` ## State storage patterns - **URL search params**: For state that should survive reloads (`?sort=price&order=ascending`) - **Snapshots**: For ephemeral UI state that should persist across navigation but not reloads ## docs/kit/25-build-and-deploy/10-building-your-app.md # Building your app Building happens in two stages when running `vite build`: 1. Vite creates optimized production build of server code, browser code, and service worker 2. An _adapter_ tunes the build for your target environment ## During the build SvelteKit loads `+page/layout(.server).js` files for analysis during build. Code that shouldn't execute at build time must check `building` from `$app/environment`: ```js import { building } from '$app/environment'; import { setupMyDatabase } from '$lib/server/database'; if (!building) { setupMyDatabase(); } export function load() { // ... } ``` ## Preview View production build locally with `vite preview`. Runs in Node, so adapter-specific features like `platform` object don't apply. ## docs/kit/25-build-and-deploy/20-adapters.md # Adapters Adapters are plugins that prepare your SvelteKit app for deployment to specific platforms. ## Official Adapters - `@sveltejs/adapter-cloudflare` - Cloudflare Workers/Pages - `@sveltejs/adapter-netlify` - Netlify - `@sveltejs/adapter-node` - Node servers - `@sveltejs/adapter-static` - Static site generation - `@sveltejs/adapter-vercel` - Vercel ## Usage Configure in `svelte.config.js`: ```js import adapter from 'svelte-adapter-foo'; /** @type {import('@sveltejs/kit').Config} */ const config = { kit: { adapter: adapter({ // adapter options go here }) } }; export default config; ``` ## Platform Context Adapters can provide platform-specific data via the `platform` property in `RequestEvent` (available in hooks and server routes). ## docs/kit/25-build-and-deploy/55-single-page-apps.md # Single-page apps Turn any SvelteKit app into a client-rendered SPA by disabling SSR: ```js /// file: src/routes/+layout.js export const ssr = false; ``` > [!NOTE] Not recommended: harms SEO, slows perceived performance, breaks accessibility when JavaScript fails. ## Usage with adapter-static For apps without server-side logic, use `adapter-static` with a fallback page: ```js // @errors: 2307 /// file: svelte.config.js import adapter from '@sveltejs/adapter-static'; export default { kit: { adapter: adapter({ fallback: '200.html' // may differ from host to host }) } }; ``` The fallback page loads your app and navigates to the correct route. Common names: `200.html`, `index.html` - check your host's documentation. ## Apache Add `static/.htaccess` for Apache hosting: ``` RewriteEngine On RewriteBase / RewriteRule ^200\.html$ - [L] RewriteCond %{REQUEST_FILENAME} !-f RewriteCond %{REQUEST_FILENAME} !-d RewriteRule . /200.html [L] ``` ## Prerendering individual pages Re-enable SSR for specific pages: ```js /// file: src/routes/my-prerendered-page/+page.js export const prerender = true; export const ssr = true; ``` ## docs/kit/30-advanced/10-advanced-routing.md # Advanced routing ## Rest parameters Use rest syntax for unknown number of route segments: ```bash /[org]/[repo]/tree/[branch]/[...file] ``` Request `/sveltejs/kit/tree/main/documentation/docs/04-advanced-routing.md` results in: ```js // @noErrors { org: 'sveltejs', repo: 'kit', branch: 'main', file: 'documentation/docs/04-advanced-routing.md' } ``` > `src/routes/a/[...rest]/z/+page.svelte` matches `/a/z`, `/a/b/z`, `/a/b/c/z` etc. Validate rest parameter values using matchers. ### 404 pages Create custom 404s with rest parameters: ```tree src/routes/ ├ marx-brothers/ | ├ [...path]/ │ ├ chico/ │ ├ harpo/ │ ├ groucho/ │ └ +error.svelte └ +error.svelte ``` ```js /// file: src/routes/marx-brothers/[...path]/+page.js import { error } from '@sveltejs/kit'; /** @type {import('./$types').PageLoad} */ export function load(event) { error(404, 'Not Found'); } ``` ## Optional parameters Make parameters optional with double brackets: `[[lang]]/home` matches both `home` and `en/home`. > Optional parameters cannot follow rest parameters. ## Matching Validate route parameters with matchers: ```js /// file: src/params/fruit.js /** * @param {string} param * @return {param is ('apple' | 'orange')} * @satisfies {import('@sveltejs/kit').ParamMatcher} */ export function match(param) { return param === 'apple' || param === 'orange'; } ``` Use in routes: `src/routes/fruits/[page=fruit]` > Matchers run on server and browser. ## Sorting Route priority (highest to lowest): - More specific routes - Parameters with matchers `[name=type]` over `[name]` - `[[optional]]` and `[...rest]` have lowest priority - Alphabetical for ties ## Encoding Use hex escape sequences for special characters: - `\` — `[x+5c]` - `/` — `[x+2f]` - `:` — `[x+3a]` - `*` — `[x+2a]` - `?` — `[x+3f]` - `"` — `[x+22]` - `<` — `[x+3c]` - `>` — `[x+3e]` - `|` — `[x+7c]` - `#` — `[x+23]` - `%` — `[x+25]` - `[` — `[x+5b]` - `]` — `[x+5d]` - `(` — `[x+28]` - `)` — `[x+29]` Unicode escape sequences: `[u+nnnn]` (equivalent routes): ``` src/routes/[u+d83e][u+dd2a]/+page.svelte src/routes/🤪/+page.svelte ``` ## Advanced layouts ### (group) Group routes with different layouts using parentheses: ```tree src/routes/ │ (app)/ │ ├ dashboard/ │ ├ item/ │ └ +layout.svelte │ (marketing)/ │ ├ about/ │ ├ testimonials/ │ └ +layout.svelte ├ admin/ └ +layout.svelte ``` Groups don't affect URL paths. `/admin` doesn't inherit group layouts. ### +page@ Break out of layout hierarchy with `@`: ```tree src/routes/ ├ (app)/ │ ├ item/ │ │ ├ [id]/ │ │ │ ├ embed/ │ │ │ │ └ +page@(app).svelte │ │ │ └ +layout.svelte │ │ └ +layout.svelte │ └ +layout.svelte └ +layout.svelte ``` Options: - `+page@[id].svelte` - inherits from `[id]/+layout.svelte` - `+page@item.svelte` - inherits from `item/+layout.svelte` - `+page@(app).svelte` - inherits from `(app)/+layout.svelte` - `+page@.svelte` - inherits from root layout ### +layout@ Layouts can also break out of hierarchy: ``` src/routes/ ├ (app)/ │ ├ item/ │ │ ├ [id]/ │ │ │ ├ embed/ │ │ │ │ └ +page.svelte // uses (app)/item/[id]/+layout.svelte │ │ │ ├ +layout.svelte // inherits from (app)/item/+layout@.svelte │ │ │ └ +page.svelte // uses (app)/item/+layout@.svelte │ │ └ +layout@.svelte // inherits from root layout, skipping (app)/+layout.svelte │ └ +layout.svelte └ +layout.svelte ``` ### When to use layout groups Alternative approach using composition: ```svelte {@render children()} ``` ```js /// file: src/routes/nested/route/+layout.js // @filename: ambient.d.ts declare module "$lib/reusable-load-function" { export function reusableLoad(event: import('@sveltejs/kit').LoadEvent): Promise>; } // @filename: index.js //cut import { reusableLoad } from '$lib/reusable-load-function'; /** @type {import('./$types').PageLoad} */ export function load(event) { // Add additional logic here, if needed return reusableLoad(event); } ``` ## docs/kit/30-advanced/20-hooks.md # Hooks App-wide functions that SvelteKit calls in response to specific events. Three optional hook files: - `src/hooks.server.js` — server hooks - `src/hooks.client.js` — client hooks - `src/hooks.js` — universal hooks (both client and server) Code runs when app starts up, useful for initializing database clients. ## Server hooks ### handle Runs on every server request. Receives `event` and `resolve` function. ```js /// file: src/hooks.server.js /** @type {import('@sveltejs/kit').Handle} */ export async function handle({ event, resolve }) { if (event.url.pathname.startsWith('/custom')) { return new Response('custom response'); } const response = await resolve(event); return response; } ``` Default: `({ event, resolve }) => resolve(event)` #### locals Add custom data to `event.locals` for handlers and server `load` functions: ```js /// file: src/hooks.server.js /** @type {import('@sveltejs/kit').Handle} */ export async function handle({ event, resolve }) { event.locals.user = await getUserInformation(event.cookies.get('sessionid')); const response = await resolve(event); // Note that modifying response headers isn't always safe. // Response objects can have immutable headers // (e.g. Response.redirect() returned from an endpoint). // Modifying immutable headers throws a TypeError. // In that case, clone the response or avoid creating a // response object with immutable headers. response.headers.set('x-custom-header', 'potato'); return response; } ``` #### resolve options Second parameter to `resolve()`: - `transformPageChunk` — transform HTML chunks - `filterSerializedResponseHeaders` — filter headers in serialized responses - `preload` — determine which files to preload ```js /// file: src/hooks.server.js /** @type {import('@sveltejs/kit').Handle} */ export async function handle({ event, resolve }) { const response = await resolve(event, { transformPageChunk: ({ html }) => html.replace('old', 'new'), filterSerializedResponseHeaders: (name) => name.startsWith('x-'), preload: ({ type, path }) => type === 'js' || path.includes('/important/') }); return response; } ``` ### handleFetch Modify `event.fetch` calls on server/during prerendering: ```js /// file: src/hooks.server.js /** @type {import('@sveltejs/kit').HandleFetch} */ export async function handleFetch({ request, fetch }) { if (request.url.startsWith('https://api.yourapp.com/')) { // clone the original request, but change the URL request = new Request( request.url.replace('https://api.yourapp.com/', 'http://localhost:9999/'), request ); } return fetch(request); } ``` For sibling subdomains, manually include cookies: ```js /// file: src/hooks.server.js /** @type {import('@sveltejs/kit').HandleFetch} */ export async function handleFetch({ event, request, fetch }) { if (request.url.startsWith('https://api.my-domain.com/')) { request.headers.set('cookie', event.request.headers.get('cookie')); } return fetch(request); } ``` ## Shared hooks Available in both `src/hooks.server.js` and `src/hooks.client.js`: ### handleError Handle unexpected errors. Log errors and return safe user representation: ```js /// file: src/hooks.server.js /** @type {import('@sveltejs/kit').HandleServerError} */ export async function handleError({ error, event, status, message }) { const errorId = crypto.randomUUID(); // example integration with https://sentry.io/ Sentry.captureException(error, { extra: { event, errorId, status } }); return { message: 'Whoops!', errorId }; } ``` Customize error shape with `App.Error` interface: ```ts /// file: src/app.d.ts declare global { namespace App { interface Error { message: string; errorId: string; } } } export {}; ``` ### init Runs once when server starts or app starts in browser: ```js /// file: src/hooks.server.js /** @type {import('@sveltejs/kit').ServerInit} */ export async function init() { await db.connect(); } ``` ## Universal hooks In `src/hooks.js`, run on both server and client: ### reroute Change URL-to-route translation before `handle`: ```js /// file: src/hooks.js /** @type {Record} */ const translated = { '/en/about': '/en/about', '/de/ueber-uns': '/de/about', '/fr/a-propos': '/fr/about', }; /** @type {import('@sveltejs/kit').Reroute} */ export function reroute({ url }) { if (url.pathname in translated) { return translated[url.pathname]; } } ``` Can be async (use carefully): ```js /// file: src/hooks.js /** @type {import('@sveltejs/kit').Reroute} */ export async function reroute({ url, fetch }) { // Ask a special endpoint within your app about the destination if (url.pathname === '/api/reroute') return; const api = new URL('/api/reroute', url); api.searchParams.set('pathname', url.pathname); const result = await fetch(api).then(r => r.json()); return result.pathname; } ``` ### transport Pass custom types across server/client boundary: ```js /// file: src/hooks.js /** @type {import('@sveltejs/kit').Transport} */ export const transport = { Vector: { encode: (value) => value instanceof Vector && [value.x, value.y], decode: ([x, y]) => new Vector(x, y) } }; ``` ## docs/kit/30-advanced/25-errors.md # Errors SvelteKit handles expected and unexpected errors differently based on where they occur and request type. ## Error Objects All errors are `{ message: string }` objects by default. Add custom properties via TypeScript interface redefinition. ## Expected Errors Created with `error()` helper from `@sveltejs/kit`: ```js /// file: src/routes/blog/[slug]/+page.server.js import { error } from '@sveltejs/kit'; import * as db from '$lib/server/database'; /** @type {import('./$types').PageServerLoad} */ export async function load({ params }) { const post = await db.getPost(params.slug); if (!post) { error(404, { message: 'Not found' }); } return { post }; } ``` Renders nearest `+error.svelte` component with `page.error` containing the error object: ```svelte

{page.error.message}

``` Add custom properties: ```js error(404, { message: 'Not found', code: 'NOT_FOUND' }); ``` Or use string shorthand: ```js error(404, 'Not found'); ``` ## Unexpected Errors Any other exceptions during request handling. Messages/stack traces not exposed to users. Default user-facing error: `{ "message": "Internal Error" }` Goes through `handleError` hook for custom handling/logging. ## Responses **In `handle` or `+server.js`**: Returns fallback error page or JSON based on `Accept` headers. **In `load` functions**: Renders nearest `+error.svelte` component. **Root layout errors**: Uses fallback error page. ### Custom Error Page Create `src/error.html`: ```html %sveltekit.error.message%

My custom error page

Status: %sveltekit.status%

Message: %sveltekit.error.message%

``` ## Type Safety Define custom error shape: ```ts /// file: src/app.d.ts declare global { namespace App { interface Error { code: string; id: string; } } } export {}; ``` Always includes `message: string` property. ## docs/kit/30-advanced/30-link-options.md # Link options SvelteKit uses `
` elements for navigation. Customize link behavior with `data-sveltekit-*` attributes on the link or parent element. These also apply to `
` elements with `method="GET"`. ## data-sveltekit-preload-data Preloads page data on hover/touch before click occurs. Values: - `"hover"` - preload on mouse hover (desktop) or touchstart (mobile) - `"tap"` - preload on touchstart/mousedown only ```html
%sveltekit.body%
``` ```html
Get current stonk values ``` Ignored when `navigator.connection.saveData` is `true`. ## data-sveltekit-preload-code Preloads page code only. Values (decreasing eagerness): - `"eager"` - preload immediately - `"viewport"` - preload when entering viewport - `"hover"` - preload on hover/touch - `"tap"` - preload on touchstart/mousedown Only affects links present in DOM after navigation. Added later links use hover/tap only. ## data-sveltekit-reload Forces full-page navigation instead of SvelteKit handling. ```html Path ``` Links with `rel="external"` get same treatment and are ignored during prerendering. ## data-sveltekit-replacestate Replaces current history entry instead of creating new one. ```html Path ``` ## data-sveltekit-keepfocus Prevents focus reset after navigation. ```html
``` Avoid on links. Only use on elements that exist after navigation. ## data-sveltekit-noscroll Prevents scroll position reset after navigation. ```html Path ``` ## Disabling options Use `"false"` value to disable inherited options: ```html
a b c
d e f
``` Conditional attributes: ```svelte
``` ## docs/kit/30-advanced/40-service-workers.md # Service workers Service workers act as proxy servers handling network requests. Enable offline support and speed up navigation by precaching built JS/CSS. ## Setup Create `src/service-worker.js` (or `src/service-worker/index.js`) - it's automatically bundled and registered. Default registration: ```js if ('serviceWorker' in navigator) { addEventListener('load', function () { navigator.serviceWorker.register('./path/to/service-worker.js'); }); } ``` ## $service-worker module Access paths to static assets, build files, prerendered pages, app version, and base path. Example - cache app eagerly, other requests on-demand: ```js // @errors: 2339 /// import { build, files, version } from '$service-worker'; // Create a unique cache name for this deployment const CACHE = `cache-${version}`; const ASSETS = [ ...build, // the app itself ...files // everything in `static` ]; self.addEventListener('install', (event) => { // Create a new cache and add all files to it async function addFilesToCache() { const cache = await caches.open(CACHE); await cache.addAll(ASSETS); } event.waitUntil(addFilesToCache()); }); self.addEventListener('activate', (event) => { // Remove previous cached data from disk async function deleteOldCaches() { for (const key of await caches.keys()) { if (key !== CACHE) await caches.delete(key); } } event.waitUntil(deleteOldCaches()); }); self.addEventListener('fetch', (event) => { // ignore POST requests etc if (event.request.method !== 'GET') return; async function respond() { const url = new URL(event.request.url); const cache = await caches.open(CACHE); // `build`/`files` can always be served from the cache if (ASSETS.includes(url.pathname)) { const response = await cache.match(url.pathname); if (response) { return response; } } // for everything else, try the network first, but // fall back to the cache if we're offline try { const response = await fetch(event.request); // if we're offline, fetch can return a value that is not a Response // instead of throwing - and we can't pass this non-Response to respondWith if (!(response instanceof Response)) { throw new Error('invalid response from fetch'); } if (response.status === 200) { cache.put(event.request, response.clone()); } return response; } catch (err) { const response = await cache.match(event.request); if (response) { return response; } // if there's no cache, then just error out // as there is nothing we can do to respond to this request throw err; } } event.respondWith(respond()); }); ``` **Gotcha:** Be careful caching large assets - browsers empty caches when full. ## Development Service worker bundled for production only. For dev with manual registration: ```js import { dev } from '$app/environment'; navigator.serviceWorker.register('/service-worker.js', { type: dev ? 'module' : 'classic' }); ``` **Note:** `build` and `prerendered` are empty arrays in development. ## Type safety Add to top of `service-worker.js`: ```original-js /// /// /// /// const sw = /** @type {ServiceWorkerGlobalScope} */ (/** @type {unknown} */ (self)); ``` Use `sw` instead of `self`. For `$env/static/public` imports, add `/// `. ## docs/kit/30-advanced/50-server-only-modules.md # Server-only modules SvelteKit prevents accidental import of sensitive data into frontend code through server-only modules. ## Private environment variables `$env/static/private` and `$env/dynamic/private` modules can only be imported in server-only modules like `hooks.server.js` or `+page.server.js`. ## Server-only utilities `$app/server` module (contains `read` function for filesystem access) can only be imported by server code. ## Your modules Make modules server-only by: - Adding `.server` to filename: `secrets.server.js` - Placing in `$lib/server`: `$lib/server/secrets.js` ## How it works SvelteKit errors on any import chain from public code to server-only code: ```js /// file: $lib/server/secrets.js export const atlantisCoordinates = [/* redacted */]; ``` ```js /// file: src/routes/utils.js export { atlantisCoordinates } from '$lib/server/secrets.js'; export const add = (a, b) => a + b; ``` ```html /// file: src/routes/+page.svelte ``` **Error:** ``` Cannot import $lib/server/secrets.js into public-facing code: src/routes/+page.svelte src/routes/utils.js $lib/server/secrets.js ``` Even though only `add` is used, the entire import chain is unsafe. Works with dynamic imports, including interpolated ones like ``await import(`./${foo}.js`)``. > **Note:** Detection disabled during testing (`process.env.TEST === 'true'`). ## docs/kit/30-advanced/65-snapshots.md # Snapshots Preserves ephemeral DOM state (scroll positions, input values) across navigation. ## Basic Usage Export `snapshot` object with `capture` and `restore` methods from `+page.svelte` or `+layout.svelte`: ```svelte