## docs/kit/10-getting-started/10-introduction.md # Introduction ## What is SvelteKit? Framework for building robust, performant web apps using Svelte. Similar to Next (React) or Nuxt (Vue). ## What is Svelte? UI component framework. Compiler converts components to JavaScript (renders HTML) and CSS. See [Svelte tutorial](/tutorial) to learn more. ## SvelteKit vs Svelte **Svelte**: Renders UI components only. **SvelteKit**: Full-featured framework providing: - Router - [Build optimizations](https://vitejs.dev/guide/features.html#build-optimizations) - [Offline support](service-workers) - [Preloading](link-options#data-sveltekit-preload-data) - [Configurable rendering](page-options): [SSR](glossary#SSR), [CSR](glossary#CSR), [prerendering](glossary#Prerendering) - [Image optimization](images) - [HMR](https://github.com/sveltejs/vite-plugin-svelte/blob/main/docs/config.md#hot) via [Vite](https://vitejs.dev/) See [project types](project-types) for application examples. ## docs/kit/10-getting-started/20-creating-a-project.md # Creating a project ## Quick start ```sh npx sv create my-app cd my-app npm run dev ``` First command scaffolds project with optional TypeScript/tooling setup. `npm run dev` starts dev server on localhost:5173. ## Core concepts - Each page = Svelte component - Add files to `src/routes` to create pages - Server-rendered first visit → client-side app takeover ## Editor setup Use VS Code with Svelte extension (other editors supported). ## docs/kit/10-getting-started/25-project-types.md # Project Types SvelteKit offers configurable rendering. Rendering settings are not mutually exclusive - choose optimal rendering per route. Adapter choice and config control build/deploy/render behavior. ## Default Rendering First page: [SSR](glossary#SSR). Subsequent pages: [CSR](glossary#CSR). SSR improves SEO and initial load. CSR updates pages without rerendering common components (faster, no flash). Also called [transitional apps](https://www.youtube.com/watch?v=860d8usGC0o). ## Static Site Generation Use [`adapter-static`](adapter-static) for full [prerendering](glossary#Prerendering) ([SSG](glossary#SSG)). Or use [prerender option](page-options#prerender) to prerender some pages and dynamically render others with different adapter. For very large sites: [`adapter-vercel`](adapter-vercel) supports [ISR](adapter-vercel#Incremental-Static-Regeneration). ## Single-Page App [SPAs](glossary#SPA) use [CSR](glossary#CSR) exclusively. See [building SPAs](single-page-apps). If no backend or [separate backend](#Separate-backend), ignore `server` file docs. ## Multi-Page App Not typical for SvelteKit. Options: - [`csr = false`](page-options#csr) removes JS, renders subsequent links on server - [`data-sveltekit-reload`](link-options#data-sveltekit-reload) renders specific links on server ## Separate Backend For backends in Go, Java, PHP, Ruby, Rust, C#, etc: - **Recommended**: Deploy frontend separately using `adapter-node` or serverless adapter - Alternative: Deploy as [SPA](single-page-apps) served by backend (worse SEO/performance) Ignore `server` file docs. See [FAQ on backend API calls](faq#How-do-I-use-a-different-backend-API-server). ## Serverless App [`adapter-auto`](adapter-auto) auto-detects platforms. Or use [`adapter-vercel`](adapter-vercel), [`adapter-netlify`](adapter-netlify), [`adapter-cloudflare`](adapter-cloudflare). [Community adapters](/packages#sveltekit-adapters) support most serverless environments. Some adapters offer `edge` option for [edge rendering](glossary#Edge). ## Your Own Server Use [`adapter-node`](adapter-node) for VPS/server deployment. ## Container Use [`adapter-node`](adapter-node) for Docker/LXC containers. ## Library Create library for other Svelte apps with [`@sveltejs/package`](packaging) add-on. Choose library option in [`sv create`](/docs/cli/sv-create). ## Offline App Full [service worker](service-workers) support for offline apps and [PWAs](glossary#PWA). ## Mobile App Turn [SvelteKit SPA](single-page-apps) into mobile app with [Tauri](https://v2.tauri.app/start/frontend/sveltekit/) or [Capacitor](https://capacitorjs.com/solution/svelte). Camera, geolocation, push notifications via plugins. Platforms use local web server. Consider [`bundleStrategy: 'single'`](configuration#output) to limit requests (Capacitor uses HTTP/1, limiting concurrent connections). ## Desktop App Turn [SvelteKit SPA](single-page-apps) into desktop app with [Tauri](https://v2.tauri.app/start/frontend/sveltekit/), [Wails](https://wails.io/docs/guides/sveltekit/), or [Electron](https://www.electronjs.org/). ## Browser Extension Use [`adapter-static`](adapter-static) or [community adapters](/packages#sveltekit-adapters) for browser extensions. ## Embedded Device Svelte runs on low-power devices. For microcontrollers/TVs with limited concurrent connections, use [`bundleStrategy: 'single'`](configuration#output). ## docs/kit/10-getting-started/30-project-structure.md # Project Structure ## Directory Layout ```tree my-project/ ├ src/ │ ├ lib/ │ │ ├ server/ # Server-only code │ │ └ [lib files] # Utilities/components, import via $lib │ ├ params/ # Param matchers │ ├ routes/ # App routes │ ├ app.html # Page template │ ├ error.html # Error page │ ├ hooks.client.js │ ├ hooks.server.js │ ├ service-worker.js │ └ instrumentation.server.js ├ static/ # Static assets (served as-is) ├ tests/ ├ package.json ├ svelte.config.js ├ tsconfig.json └ vite.config.js ``` ## Key Files ### src/lib - Import via `$lib` alias - `server/` subdirectory: server-only code via `$lib/server` alias (client imports prevented) ### src/app.html Page template with placeholders: - `%sveltekit.head%` — ``, `
{comment.content}
{/each} {:catch error}error loading comments: {error.message}
{/await} ``` Handle rejections to avoid unhandled promise errors. Attach noop-`catch` or use SvelteKit's `fetch`. **Limitations:** - No streaming on platforms without support (AWS Lambda, Firebase) - Can't `setHeaders` or redirect inside streamed promise - Don't return promises from universal `load` if SSR enabled - Headers/status can't change after streaming starts ## Parallel loading All `load` functions run concurrently. Server `load` results grouped into single response. ## Rerunning load functions `load` reruns when dependencies change: - Referenced `params` property changes - Referenced `url` property changes (`pathname`, `search`) - `url.searchParams.get/getAll/has()` parameter changes - `await parent()` called and parent reruns - Dependency marked invalid via `fetch(url)` or `depends(url)` + `invalidate(url)` - `invalidateAll()` called ### Untracking dependencies ```js /// file: src/routes/+page.js /** @type {import('./$types').PageLoad} */ export async function load({ untrack, url }) { if (untrack(() => url.pathname === '/')) { return { message: 'Welcome!' }; } } ``` ### 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() }; } ``` ```svelterandom number: {data.number}
``` ## Auth implications - Layout `load` doesn't run on every request (e.g., client-side nav) - Layout and page `load` run concurrently unless `await parent()` called - If layout `load` throws, page `load` runs but client won't receive data **Strategies:** - Use hooks to protect routes before `load` runs - Put auth guards in `+page.server.js` for route-specific protection - Avoid auth in `+layout.server.js` unless all children call `await parent()` ## 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 exports _actions_ to `POST` data to the server using ` ``` **From other pages:** Add `action` attribute pointing to the page: ```html ``` > Actions always use `POST` requests. ## Named Actions ```js /// file: src/routes/login/+page.server.js export const actions = { login: async (event) => { // TODO log the user in }, register: async (event) => { // TODO register the user } }; ``` **Invoke with query parameter:** ```svelte ``` > Can't have default actions next to named actions - query parameter persists in URL. ## Anatomy of an Action Actions receive `RequestEvent`, read data with `request.formData()`. Return data available via `form` prop and `page.form`. ```js /// file: src/routes/login/+page.server.js import * as db from '$lib/server/db'; export async function load({ cookies }) { const user = await db.getUserFromSession(cookies.get('sessionid')); return { user }; } 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 }; }, register: async (event) => { // TODO register the user } }; ``` ```svelte {#if form?.success}Successfully logged in! Welcome back, {data.user.name}
{/if} ``` ### Validation Errors Use `fail()` to return HTTP status code (400/422) with validation errors: ```js /// file: src/routes/login/+page.server.js import { fail } from '@sveltejs/kit'; import * as db from '$lib/server/db'; 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 }; }, register: async (event) => { // TODO register the user } }; ``` ```svelte /// file: src/routes/login/+page.svelte ``` ### Redirects ```js /// file: src/routes/login/+page.server.js import { fail, redirect } from '@sveltejs/kit'; import * as db from '$lib/server/db'; export const actions = { login: async ({ cookies, request, url }) => { const data = await request.formData(); const email = data.get('email'); const password = data.get('password'); const user = await db.getUser(email); if (!user) { return fail(400, { email, missing: true }); } if (user.password !== db.hash(password)) { return fail(400, { email, incorrect: true }); } cookies.set('sessionid', await db.createSession(user), { path: '/' }); if (url.searchParams.has('redirectTo')) { redirect(303, url.searchParams.get('redirectTo')); } return { success: true }; }, register: async (event) => { // TODO register the user } }; ``` ## Loading Data After action runs, page re-renders with action's return value as `form` prop. Page `load` functions run after action completes. **Important:** `handle` runs before action and doesn't rerun before `load`. Update `event.locals` in action if needed: ```js /// file: src/hooks.server.js export async function handle({ event, resolve }) { event.locals.user = await getUser(event.cookies.get('sessionid')); return resolve(event); } ``` ```js /// file: src/routes/account/+page.server.js export function load(event) { return { user: event.locals.user }; } export const actions = { logout: async (event) => { event.cookies.delete('sessionid', { path: '/' }); event.locals.user = null; } }; ``` ## Progressive Enhancement ### use:enhance ```svelte /// file: src/routes/login/+page.svelte ``` **Must use `deserialize()` not `JSON.parse()`** - supports `Date` and `BigInt`. **POST to action with `+server.js` present:** ```js const response = await fetch(this.action, { method: 'POST', body: data, headers: { 'x-sveltekit-action': 'true' } }); ``` ## Alternatives Use `+server.js` for JSON APIs: ```svelte ``` ```js /// file: src/routes/api/ci/+server.js export function POST() { // do something } ``` ## GET vs POST Use `method="GET"` for forms that don't POST data (e.g., search). SvelteKit treats them like `` elements using client-side router: ```html ``` Navigates to `/search?q=...`, invokes `load` but not action. Supports `data-sveltekit-*` attributes like `` elements. ## docs/kit/20-core-concepts/40-page-options.md # Page Options SvelteKit renders components server-side first, sends HTML to client, then hydrates for interactivity. Control this per-page via `+page.js`/`+page.server.js` or per-group via `+layout.js`/`+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; ``` Or set in root layout and opt-out specific pages: ```js /// file: +page.js/+page.server.js/+server.js export const prerender = false; ``` Third option excludes from SSR manifest but still prerenders: ```js /// file: +page.js/+page.server.js/+server.js export const prerender = 'auto'; ``` Prerenderer starts at root, follows `` links to find pages. Configure entry points via `config.kit.prerender.entries` or [`entries`](#entries) function. ### Prerendering server routes `+server.js` files inherit `prerender` from pages that fetch from them unless explicitly set. ```js /// file: +page.js export const prerender = true; /** @type {import('./$types').PageLoad} */ export async function load({ fetch }) { const res = await fetch('/my-server-route.json'); return await res.json(); } ``` ### When not to prerender **Rule:** Two users hitting a page directly must get identical server content. - Don't prerender personalized content - `url.searchParams` forbidden during prerender (use in browser only, e.g. `onMount`) - Pages with [actions](form-actions) can't be prerendered ### Route conflicts Avoid directory/file name conflicts. Use file extensions: `foo.json/+server.js` and `foo/bar.json/+server.js` create `foo.json` and `foo/bar.json`. Pages write `foo/index.html`. ### Troubleshooting Error "routes marked as prerenderable, but were not prerendered" means crawler didn't reach the route. **Fixes:** - Add links from `config.kit.prerender.entries` or [`entries`](#entries) - Ensure links exist on prerendered pages - Change to `export const prerender = 'auto'` ## entries Define dynamic route parameters for prerendering: ```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; ``` Can be `async` to fetch from CMS/database. ## ssr Disable server-side rendering: ```js /// file: +page.js export const ssr = false; // If both `ssr` and `csr` are `false`, nothing will be rendered! ``` Renders empty shell instead of full HTML. Not recommended for most cases. **Note:** If all page options are static values, SvelteKit evaluates them statically. Otherwise, it imports files at build/runtime, so browser-only code must not run on module load. ## csr Disable client-side rendering (no JavaScript shipped): ```js /// file: +page.js export const csr = false; // If both `csr` and `ssr` are `false`, nothing will be rendered! ``` **Effects:** - Page must work with HTML/CSS only - All ` ``` ```svelteWelcome {user().name}
``` > Pass a function to `setContext` to maintain reactivity. See [$state docs](/docs/svelte/$state#Passing-state-into-functions). **Gotcha:** Context updates in child components during SSR won't affect already-rendered parents. Prefer passing state down. Without SSR, you can use shared modules directly. ## Component state is preserved Components are reused during navigation. State won't reset automatically: ```svelteReading time: {Math.round(estimatedReadingTime)} minutes
likes: {await getLikes(item.id)}
``` **Note:** Commands cannot be called during render. Prefer `form` where possible. ### Updating queries Inside command: ```js export const addLike = command(v.string(), async (id) => { await db.sql`UPDATE item SET likes = likes + 1 WHERE id = ${id}`; getLikes(id).refresh(); // or: getLikes(id).set(...) }); ``` When calling: ```ts await addLike(item.id).updates(getLikes(item.id)); ``` Optimistic updates: ```ts await addLike(item.id).updates( getLikes(item.id).withOverride((n) => n + 1) ); ``` ## prerender Invoked at build time for static data: ```js import { prerender } from '$app/server'; export const getPosts = prerender(async () => { const posts = await db.sql`SELECT title, slug FROM post ORDER BY published_at DESC`; return posts; }); ``` Data cached using Cache API, survives reloads, cleared on new deployment. ### Prerender arguments ```js export const getPost = prerender(v.string(), async (slug) => { const [post] = await db.sql`SELECT * FROM post WHERE slug = ${slug}`; if (!post) error(404, 'Not found'); return post; }); ``` Specify inputs: ```js export const getPost = prerender( v.string(), async (slug) => { /* ... */ }, { inputs: () => ['first-post', 'second-post', 'third-post'] } ); ``` Allow dynamic calls: ```js export const getPost = prerender( v.string(), async (slug) => { /* ... */ }, { dynamic: true, inputs: () => ['first-post', 'second-post', 'third-post'] } ); ``` ## Handling validation errors Implement `handleValidationError` hook: ```js /// file: src/hooks.server.ts export function handleValidationError({ event, issues }) { return { message: 'Nice try, hacker!' }; } ``` Opt out of validation: ```ts export const getStuff = query('unchecked', async ({ id }: { id: string }) => { // shape might not match TypeScript }); ``` ## Using `getRequestEvent` Access `RequestEvent` inside `query`, `form`, `command`: ```ts import { getRequestEvent, query } from '$app/server'; export const getProfile = query(async () => { const user = await getUser(); return { name: user.name, avatar: user.avatar }; }); const getUser = query(async () => { const { cookies } = getRequestEvent(); return await findUser(cookies.get('session_id')); }); ``` **Note:** Some properties differ in remote functions: - Cannot set headers (except cookies in `form`/`command`) - `route`, `params`, `url` relate to calling page, not endpoint - Don't use for authorization checks ## Redirects Use `redirect(...)` in `query`, `form`, `prerender`. **Not** in `command` (return `{ redirect: location }` if needed). ## 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 builds (server code, browser code, service worker). Prerendering executes here. 2. An adapter tunes the build for your target environment. ## During the build Files are loaded for analysis during build. Code that shouldn't execute at build time must check `building`: ```js import { building } from '$app/environment'; import { initialiseDatabase } from '$lib/server/database'; if (!building) { initialiseDatabase(); } export function load() { // ... } ``` ## Preview your app 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 (SSG) - `@sveltejs/adapter-vercel` - Vercel Community adapters available at `/packages#sveltekit-adapters`. ## Usage Configure in `svelte.config.js`: ```js /// file: svelte.config.js // @filename: ambient.d.ts declare module 'svelte-adapter-foo' { const adapter: (opts: any) => import('@sveltejs/kit').Adapter; export default adapter; } // @filename: index.js //cut import adapter from 'svelte-adapter-foo'; /** @type {import('@sveltejs/kit').Config} */ const config = { kit: { adapter: adapter({ // adapter options go here }) } }; export default config; ``` ## Platform-Specific Context Adapters may provide platform-specific data (e.g., Cloudflare KV namespaces) via `platform` property in `RequestEvent` (available in hooks and server routes). See adapter docs for details. ## docs/kit/25-build-and-deploy/55-single-page-apps.md # Single-page apps Turn SvelteKit into a client-rendered SPA by specifying a fallback page that serves URLs not handled by prerendered pages. > [!NOTE] SPA mode forces multiple network round trips (HTML → JS → data) before showing content, harming performance and SEO. Prerender as many pages as possible, especially your homepage. If all pages can be prerendered, use [static site generation](adapter-static) instead. ## Usage Disable SSR for non-prerendered pages: ```js /// file: src/routes/+layout.js export const ssr = false; ``` If no server-side logic (`+page.server.js`, `+layout.server.js`, `+server.js`), use `adapter-static`: ```js /// file: svelte.config.js import adapter from '@sveltejs/adapter-static'; /** @type {import('@sveltejs/kit').Config} */ const config = { kit: { adapter: adapter({ fallback: '200.html' // may differ from host to host }) } }; export default config; ``` The `fallback` page is an HTML page that loads your app and navigates to the correct route. Consult your platform's docs for the correct filename (e.g. `200.html` for Surge). Avoid `index.html` as it may conflict with prerendering. > [!NOTE] Fallback page always contains absolute asset paths (starting with `/`) regardless of `paths.relative`. ## Prerendering individual pages Re-enable `ssr` + `prerender` for specific pages: ```js /// file: src/routes/my-prerendered-page/+page.js export const prerender = true; export const ssr = true; ``` No Node server needed—pages are server-rendered at build time to static `.html` files. ## Apache Add `static/.htaccess` to route requests to fallback: ```Status: %sveltekit.status%
Message: %sveltekit.error.message%
``` ## Type Safety Customize error shape via `App.Error` interface: ```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 behavior with `data-sveltekit-*` attributes on links or parent elements. Also applies to ` ``` > Avoid on links. Only use on elements that persist after navigation. ## data-sveltekit-noscroll Prevent scroll to top (0,0) after navigation. ```html Path ``` ## Disabling options Use `"false"` to disable inherited attributes: ```html ``` **Conditional:** ```svelte