# Svelte User Guide

> Svelte v5.55.4 — "Cybernetically enhanced web apps"
>
> Source-based reference for building reactive web applications with Svelte's compiler-based, runes-driven paradigm.

---

## 1. Quick Start

Get a Svelte application running in under five minutes using SvelteKit (the recommended application framework) or a standalone Vite project.

> [!TIP]
> SvelteKit is the full-stack framework built on top of Svelte. Use it for any serious project — it handles routing, SSR, code splitting, and deployment adapters out of the box. Use standalone Svelte (via Vite) only when you need to embed components into an existing application.

### 1.1 Create a SvelteKit App (Recommended)

```bash
npx sv create my-app
cd my-app
npm install
npm run dev
```

The `sv create` scaffold prompts you for TypeScript, ESLint, Prettier, Playwright, and Vitest. Accept the defaults for a batteries-included starting point.

Navigate to `http://localhost:5173` to see the running application.

### 1.2 Create a Standalone Vite + Svelte Project

For a component library or an embedded widget that doesn't need SvelteKit's router:

```bash
npm create vite@latest my-app -- --template svelte
# or for TypeScript:
npm create vite@latest my-app -- --template svelte-ts
cd my-app
npm install
npm run dev
```

### 1.3 Your First Component

Create `src/Counter.svelte`:

```svelte
<script>
  let count = $state(0);

  function increment() {
    count++;
  }
</script>

<button onclick={increment}>
  Clicked {count} {count === 1 ? 'time' : 'times'}
</button>

<style>
  button {
    font-size: 1.5rem;
    padding: 0.5rem 1rem;
    border-radius: 4px;
    cursor: pointer;
  }
</style>
```

Use it in `src/App.svelte`:

```svelte
<script>
  import Counter from './Counter.svelte';
</script>

<h1>Hello Svelte!</h1>
<Counter />
```

> [!TIP]
> Svelte 5 uses **runes** — special `$`-prefixed functions that the compiler transforms into efficient reactive code. You do not import runes; the compiler recognizes them automatically anywhere inside `.svelte` files and `.svelte.js`/`.svelte.ts` modules.

### 1.4 Mount Manually (no SvelteKit)

```javascript
// src/main.js
import { mount } from 'svelte';
import App from './App.svelte';

const app = mount(App, {
  target: document.getElementById('app'),
  props: { name: 'world' }
});
```

---

## 2. Installation

### 2.1 Requirements

| Requirement | Minimum Version |
|-------------|----------------|
| Node.js | 18.x or higher |
| npm / pnpm / yarn | any modern version |
| Browser | Modern evergreen browsers (Chrome 64+, Firefox 69+, Safari 13+, Edge 79+) |

> [!IMPORTANT]
> Svelte 5 requires **Node.js >= 18**. Older Node versions will produce build errors. Verify with `node --version` before starting.

### 2.2 Install as a Dependency

In an existing project, add Svelte and its Vite plugin:

```bash
npm install --save-dev svelte @sveltejs/vite-plugin-svelte vite
```

Configure Vite:

```javascript
// vite.config.js
import { defineConfig } from 'vite';
import { svelte } from '@sveltejs/vite-plugin-svelte';

export default defineConfig({
  plugins: [svelte()]
});
```

### 2.3 Install SvelteKit

```bash
npm install --save-dev @sveltejs/kit @sveltejs/adapter-auto
npm install svelte
```

### 2.4 TypeScript Support

Svelte has first-class TypeScript support. When using `sv create`, select TypeScript at the prompt. For manual setup:

```bash
npm install --save-dev typescript svelte-check
```

Add `svelte-check` to your package.json scripts:

```json
{
  "scripts": {
    "check": "svelte-check --tsconfig ./tsconfig.json"
  }
}
```

Run `npm run check` to type-check your `.svelte` files.

> [!NOTE]
> Inside `.svelte` files, use `<script lang="ts">` to enable TypeScript in that component's script block.

### 2.5 Editor Setup

**VS Code** (recommended): Install the [Svelte for VS Code](https://marketplace.visualstudio.com/items?itemName=svelte.svelte-vscode) extension. It provides syntax highlighting, IntelliSense, hover docs, go-to-definition, and auto-completion for runes.

**JetBrains IDEs**: Install the Svelte plugin from the JetBrains marketplace.

**Neovim / Vim**: Use `svelte-language-server` via LSP.

### 2.6 Package Structure

The `svelte` npm package exports multiple submodules:

| Submodule | Contents |
|-----------|----------|
| `svelte` | Runes, lifecycle, context, rendering, utilities |
| `svelte/store` | Writable, readable, derived stores |
| `svelte/motion` | `tweened`, `spring` |
| `svelte/transition` | `fade`, `blur`, `fly`, `slide`, `scale`, `draw`, `crossfade` |
| `svelte/animate` | `flip` |
| `svelte/easing` | All easing functions |
| `svelte/events` | Event utilities |
| `svelte/attachments` | Attachment utilities |
| `svelte/reactivity` | `SvelteDate`, `SvelteMap`, `SvelteSet`, `SvelteURL`, etc. |
| `svelte/reactivity/window` | Window-reactive values |
| `svelte/server` | `render()` for SSR |
| `svelte/compiler` | `compile()`, `parse()`, `preprocess()`, `migrate()`, `compileModule()`, `VERSION` |
| `svelte/legacy` | Legacy compatibility helpers |

> [!WARNING]
> Do not import runes (`$state`, `$derived`, etc.) from any package. They are compiler-recognized keywords, not runtime imports. Writing `import { $state } from 'svelte'` will produce an error.

---

## 3. Configuration

### 3.1 svelte.config.js

The `svelte.config.js` file at your project root controls the Svelte compiler, preprocessors, and (for SvelteKit) the deployment adapter.

```javascript
// svelte.config.js
import adapter from '@sveltejs/adapter-auto';
import { vitePreprocess } from '@sveltejs/vite-plugin-svelte';

/** @type {import('@sveltejs/kit').Config} */
const config = {
  // Preprocessors run before the Svelte compiler
  preprocess: vitePreprocess(),

  kit: {
    adapter: adapter()
  },

  // Svelte compiler options
  compilerOptions: {
    // see section 3.2
  }
};

export default config;
```

### 3.2 Compiler Options (`compilerOptions`)

These options are passed to the Svelte compiler. Set them in `svelte.config.js` under `compilerOptions`, or per-component via `<svelte:options>`.

#### Output & Mode

| Option | Type | Default | Description |
|--------|------|---------|-------------|
| `generate` | `'client'` \| `'server'` \| `false` | `'client'` | Target output: DOM, SSR, or no output (analysis only) |
| `dev` | `boolean` | `false` | Enable development checks and warnings |
| `sourcemap` | `boolean` \| `object` | `false` | Include source maps in output |
| `outputFilename` | `string` | `undefined` | JS output filename (for source maps) |
| `cssOutputFilename` | `string` | `undefined` | CSS output filename (for source maps) |

#### Component Behavior

| Option | Type | Default | Description |
|--------|------|---------|-------------|
| `runes` | `boolean` \| `undefined` | `undefined` | Force runes mode (`true`), force legacy mode (`false`), or auto-detect (`undefined`) |
| `hydratable` | `boolean` | `true` (SSR) | Emit hydration markers in SSR output |
| `css` | `'injected'` \| `'external'` \| `'none'` | `'injected'` | How component CSS is emitted |
| `namespace` | `'html'` \| `'mathml'` \| `'svg'` | `'html'` | Element namespace for top-level elements |
| `customElement` | `boolean` | `false` | Compile as a custom element |
| `accessors` | `boolean` | `false` | Generate getters/setters for props (legacy) |
| `immutable` | `boolean` | `false` | Assume objects never mutate (legacy optimization) |

#### Warnings

| Option | Type | Default | Description |
|--------|------|---------|-------------|
| `warningFilter` | `(warning) => boolean` | `undefined` | Filter which warnings are shown |

Example: disable a specific warning:

```javascript
// svelte.config.js
const config = {
  compilerOptions: {
    warningFilter: (warning) => warning.code !== 'a11y_mouse_events_have_key_events'
  }
};
```

### 3.3 `<svelte:options>` (Per-Component Overrides)

Override compiler options for a single component at the top of the file:

```svelte
<svelte:options
  runes={true}
  customElement={{ tag: 'my-widget', shadow: 'none' }}
  namespace="svg"
  css="injected"
  accessors={false}
/>
```

### 3.4 Vite Plugin Options

```javascript
// vite.config.js
import { defineConfig } from 'vite';
import { svelte } from '@sveltejs/vite-plugin-svelte';

export default defineConfig({
  plugins: [
    svelte({
      // Path to svelte.config.js (auto-discovered by default)
      configFile: 'svelte.config.js',

      // Additional extensions to treat as Svelte files
      extensions: ['.svelte'],

      // Preprocessors (if not using svelte.config.js)
      preprocess: [],

      // Compiler options (merged with svelte.config.js)
      compilerOptions: {
        dev: process.env.NODE_ENV !== 'production'
      },

      // Enable hot module replacement
      hot: true
    })
  ]
});
```

### 3.5 TypeScript Configuration

```json
// tsconfig.json (SvelteKit project)
{
  "extends": "./.svelte-kit/tsconfig.json",
  "compilerOptions": {
    "allowJs": true,
    "checkJs": true,
    "esModuleInterop": true,
    "forceConsistentCasingInFileNames": true,
    "resolveJsonModule": true,
    "skipLibCheck": true,
    "sourceMap": true,
    "strict": true
  }
}
```

### 3.6 SvelteKit Adapters

Adapters tell SvelteKit how to deploy your app:

| Adapter | Target |
|---------|--------|
| `@sveltejs/adapter-auto` | Detects Vercel, Netlify, Cloudflare, etc. |
| `@sveltejs/adapter-node` | Node.js server |
| `@sveltejs/adapter-static` | Static site (no SSR) |
| `@sveltejs/adapter-vercel` | Vercel (explicit) |
| `@sveltejs/adapter-netlify` | Netlify (explicit) |
| `@sveltejs/adapter-cloudflare` | Cloudflare Pages/Workers |

```javascript
// svelte.config.js — static site example
import adapter from '@sveltejs/adapter-static';

const config = {
  kit: {
    adapter: adapter({
      pages: 'build',
      assets: 'build',
      fallback: 'index.html', // for SPA mode
      precompress: false,
      strict: true
    })
  }
};
export default config;
```

---

## 4. Core Usage

### 4.1 Components

A Svelte component is a `.svelte` file with three optional sections:

```svelte
<script>
  // Logic: runes, imports, variables, functions
</script>

<!-- Template: HTML + template syntax -->
<div>Content</div>

<style>
  /* Scoped CSS — only applies to this component */
  div { color: red; }
</style>
```

All three sections are optional. The order is conventional but not enforced.

#### Module-level script

For code that runs once per module (not once per instance), use `<script module>`:

```svelte
<script module>
  // Runs once when module is loaded — shared across instances
  let instanceCount = 0;

  export function getInstanceCount() {
    return instanceCount;
  }
</script>

<script>
  import { onMount } from 'svelte';
  // Runs once per component instance
  instanceCount++;
</script>
```

### 4.2 Runes — The Reactivity System

Svelte 5's reactivity is driven by **runes**: compiler-recognized `$`-prefixed constructs that declare reactive intent. Runes work in `.svelte` files and in `.svelte.js` / `.svelte.ts` modules.

#### `$state` — Reactive State

```svelte
<script>
  let count = $state(0);
  let user = $state({ name: 'Alice', age: 30 });
  let items = $state([1, 2, 3]);
</script>

<p>{count}</p>
<button onclick={() => count++}>Increment</button>

<!-- Deep reactivity: mutations to nested properties work -->
<button onclick={() => user.name = 'Bob'}>Rename</button>
<button onclick={() => items.push(4)}>Add item</button>
```

The `$state` rune creates a **deeply reactive proxy** for objects and arrays. Mutations at any depth trigger updates.

> [!NOTE]
> `$state` uses JavaScript `Proxy` under the hood. Logging a state object shows a Proxy — use `$state.snapshot()` to get a plain object for debugging or serialization.

##### `$state.raw` — Non-deep Reactive State

For large objects or arrays where you only need to track top-level reassignment (not mutations):

```svelte
<script>
  let data = $state.raw({ items: [1, 2, 3] });

  function reset() {
    // Must reassign — mutation won't trigger update
    data = { items: [] };
  }
</script>
```

##### `$state.snapshot` — Plain Copy of State

```svelte
<script>
  let list = $state([1, 2, 3]);

  function logList() {
    // Returns a plain array, not a Proxy
    const plain = $state.snapshot(list);
    console.log(plain); // [1, 2, 3]
    localStorage.setItem('list', JSON.stringify(plain));
  }
</script>
```

##### `$state.eager` — Latest Value During Suspension

For use inside `{#await}` blocks when you need the newest value even before the promise settles:

```svelte
<script>
  let value = $state.eager(0);
</script>
```

> [!WARNING]
> `$state.eager` combined with `$effect.pending()` has a known bug in 5.55.4 (issue #18095). Until fixed, avoid this combination in production.

#### `$derived` — Computed Values

```svelte
<script>
  let width = $state(10);
  let height = $state(20);
  let area = $derived(width * height);
  let perimeter = $derived(2 * (width + height));
</script>

<p>Area: {area}</p>
<p>Perimeter: {perimeter}</p>

<input type="number" bind:value={width} />
<input type="number" bind:value={height} />
```

`$derived` is lazy and cached — it only recomputes when its dependencies change.

##### `$derived.by` — Complex Derivations

Use `$derived.by(fn)` when the expression is too complex for a single inline expression:

```svelte
<script>
  let numbers = $state([3, 1, 4, 1, 5, 9, 2, 6]);

  let stats = $derived.by(() => {
    const sorted = [...numbers].sort((a, b) => a - b);
    const sum = numbers.reduce((a, b) => a + b, 0);
    const mean = sum / numbers.length;
    const median = sorted[Math.floor(sorted.length / 2)];
    return { sum, mean, median, min: sorted[0], max: sorted.at(-1) };
  });
</script>

<p>Sum: {stats.sum}, Mean: {stats.mean.toFixed(2)}</p>
```

> [!IMPORTANT]
> Never use side effects inside `$derived`. It runs in a tracking context and is expected to be pure. Use `$effect` for side effects.

#### `$effect` — Side Effects

```svelte
<script>
  let count = $state(0);

  $effect(() => {
    // Runs after the DOM updates whenever count changes
    document.title = `Count: ${count}`;

    // Optional cleanup — returned function runs before next effect execution
    return () => {
      document.title = 'App';
    };
  });
</script>
```

Effects automatically track which reactive values they access. When any of those values change, the effect re-runs (after DOM update).

##### `$effect.pre` — Before DOM Update

```svelte
<script>
  import { tick } from 'svelte';
  let messages = $state([]);
  let div;

  $effect.pre(() => {
    // Runs before DOM update
    // Useful for reading DOM layout before it changes
    const scrolledToBottom =
      div && div.offsetHeight + div.scrollTop > div.scrollHeight - 20;
    
    if (scrolledToBottom) {
      // Will be applied after DOM updates
      tick().then(() => {
        div.scrollTop = div.scrollHeight;
      });
    }
  });
</script>

<div bind:this={div} style="overflow: auto; height: 300px;">
  {#each messages as msg}
    <p>{msg}</p>
  {/each}
</div>
```

##### `$effect.tracking` — Check Reactive Context

```svelte
<script>
  function maybeTrack() {
    if ($effect.tracking()) {
      // We're inside an effect or derived — safe to read reactive state
      console.log('Inside tracking context');
    }
  }
</script>
```

##### `$effect.root` — Manual Cleanup Scope

Use when you need to create effects outside of component lifecycle (e.g., in utilities):

```javascript
// utils.svelte.js
import { $effect } from 'svelte'; // NOTE: still a compiler keyword, not an import

export function createLogger(getValue) {
  const cleanup = $effect.root(() => {
    $effect(() => {
      console.log('Value changed:', getValue());
    });
  });
  return cleanup; // Call to tear down all effects in this root
}
```

##### `$effect.pending` — Pending Async Boundaries

Returns the number of pending promises in the nearest `<svelte:boundary>`:

```svelte
<script>
  let pending = $derived($effect.pending());
</script>

{#if pending > 0}
  <p>Loading {pending} items...</p>
{/if}
```

#### `$props` — Component Props

```svelte
<!-- Greeting.svelte -->
<script>
  let { name, greeting = 'Hello' } = $props();
</script>

<p>{greeting}, {name}!</p>
```

Usage:

```svelte
<Greeting name="Alice" />
<Greeting name="Bob" greeting="Howdy" />
```

##### Rest Props

```svelte
<script>
  let { class: className, style, ...rest } = $props();
</script>

<div class={className} {style} {...rest}>
  <slot />
</div>
```

##### TypeScript Props

```svelte
<script lang="ts">
  interface Props {
    name: string;
    count?: number;
    onUpdate?: (value: number) => void;
  }

  let { name, count = 0, onUpdate }: Props = $props();
</script>
```

#### `$props.id` — Unique Component ID

Available since Svelte 5.20.0. Generates a unique ID per component instance — useful for accessibility attributes like `for`/`id` pairings:

```svelte
<script>
  const uid = $props.id();
</script>

<label for={uid}>Label</label>
<input id={uid} type="text" />
```

#### `$bindable` — Two-Way Bindable Props

Mark a prop as bindable so parent components can use `bind:propName`:

```svelte
<!-- TextInput.svelte -->
<script>
  let { value = $bindable('') } = $props();
</script>

<input bind:value />
```

Parent usage:

```svelte
<script>
  let text = $state('');
</script>

<TextInput bind:value={text} />
<p>You typed: {text}</p>
```

#### `$inspect` — Development Debugging

`$inspect` logs any reactive value changes in development mode. Has no effect in production builds.

```svelte
<script>
  let count = $state(0);
  let user = $state({ name: 'Alice' });

  // Logs to console whenever count or user changes
  $inspect(count, user);

  // Custom handler
  $inspect(count).with((type, value) => {
    if (type === 'update') {
      myAnalytics.track('count_changed', { value });
    }
  });
</script>
```

##### `$inspect.trace` — Effect Dependency Tracing

```svelte
<script>
  $effect(() => {
    $inspect.trace('my effect');
    // Now logs which state changes caused this effect to re-run
    doSomething(count, user);
  });
</script>
```

#### `$host` — Custom Element Access

For components compiled as custom elements, `$host()` returns the host element (`this`):

```svelte
<svelte:options customElement="my-button" />

<script>
  function dispatch() {
    $host().dispatchEvent(new CustomEvent('activate', { bubbles: true }));
  }
</script>

<button onclick={dispatch}>Activate</button>
```

### 4.3 Lifecycle Functions

Import lifecycle functions from `svelte`:

```svelte
<script>
  import { onMount, onDestroy } from 'svelte';

  let timer;

  onMount(() => {
    // Called after component is mounted to the DOM
    timer = setInterval(() => console.log('tick'), 1000);

    // Return a cleanup function (optional)
    return () => clearInterval(timer);
  });

  onDestroy(() => {
    // Called when component is about to be removed
    // Also runs during SSR (unlike onMount)
    clearInterval(timer);
  });
</script>
```

> [!NOTE]
> `onMount` does **not** run during server-side rendering. `onDestroy` **does** run during SSR. For SSR cleanup, use `onDestroy`.

#### Deprecated Lifecycle Functions

| Deprecated | Replacement |
|------------|-------------|
| `beforeUpdate(fn)` | `$effect.pre(() => ...)` |
| `afterUpdate(fn)` | `$effect(() => ...)` |
| `createEventDispatcher()` | Callback props or `$host()` |

```svelte
<!-- OLD (still works, but deprecated) -->
<script>
  import { beforeUpdate, afterUpdate, createEventDispatcher } from 'svelte';
  const dispatch = createEventDispatcher();
  beforeUpdate(() => { /* before DOM update */ });
  afterUpdate(() => { /* after DOM update */ });
</script>
<button on:click={() => dispatch('activate')}>Click</button>

<!-- NEW (Svelte 5 style) -->
<script>
  let { onactivate } = $props();
  $effect.pre(() => { /* before DOM update */ });
  $effect(() => { /* after DOM update */ });
</script>
<button onclick={() => onactivate?.()}>Click</button>
```

### 4.4 Context API

Context allows passing data down the component tree without prop drilling.

```svelte
<!-- Parent.svelte -->
<script>
  import { setContext } from 'svelte';
  import Child from './Child.svelte';

  const theme = $state({ primary: '#3498db', dark: false });

  // Key can be any value — use a Symbol for uniqueness
  const THEME_KEY = Symbol('theme');
  setContext(THEME_KEY, theme);
</script>

<Child />
```

```svelte
<!-- Child.svelte (any depth below Parent) -->
<script>
  import { getContext } from 'svelte';
  const THEME_KEY = Symbol('theme'); // Must use same key — share via module export

  const theme = getContext(THEME_KEY);
</script>

<div style:background={theme.dark ? '#333' : '#fff'}>
  <p style:color={theme.primary}>Themed content</p>
</div>
```

> [!TIP]
> Export the context key from a shared module so parent and child always use the same reference:
>
> ```javascript
> // context-keys.js
> export const THEME_KEY = Symbol('theme');
> export const USER_KEY = Symbol('user');
> ```

#### Context Utilities

```javascript
import { setContext, getContext, hasContext, getAllContexts, createContext } from 'svelte';

// Check if context exists before reading
if (hasContext('key')) {
  const value = getContext('key');
}

// Get all contexts (returns a Map)
const all = getAllContexts();

// createContext — typed context helper
const { getContext: getTheme, setContext: setTheme } = createContext();
```

### 4.5 Template Syntax

#### Interpolation

```svelte
<p>Hello, {name}!</p>
<p>The answer is {40 + 2}</p>
<p title={tooltip}>Hover me</p>
```

#### Conditionals — `{#if}`

```svelte
{#if user.loggedIn}
  <p>Welcome back, {user.name}!</p>
{:else if user.pending}
  <p>Verifying your account...</p>
{:else}
  <a href="/login">Please log in</a>
{/if}
```

#### Loops — `{#each}`

```svelte
<!-- Basic -->
{#each items as item}
  <li>{item.name}</li>
{/each}

<!-- With index -->
{#each items as item, i}
  <li>{i + 1}. {item.name}</li>
{/each}

<!-- With key (for efficient DOM updates) -->
{#each items as item (item.id)}
  <li>{item.name}</li>
{/each}

<!-- With else (empty state) -->
{#each items as item (item.id)}
  <li>{item.name}</li>
{:else}
  <li>No items found</li>
{/each}

<!-- Destructuring -->
{#each users as { id, name, avatar } (id)}
  <div>
    <img src={avatar} alt={name} />
    <span>{name}</span>
  </div>
{/each}
```

> [!IMPORTANT]
> Always use a **key** `(item.id)` when rendering lists of dynamic items. Without a key, Svelte patches the DOM in-place, which can produce incorrect results when items are reordered, added, or removed.

> [!NOTE]
> There is a known performance regression (issue #18120) in Svelte 5.55.4 where keyed `{#each}` has slightly worse performance than non-keyed in some benchmarks. For most real-world lists, use keyed — correctness outweighs micro-benchmarks.

#### Async — `{#await}`

```svelte
<script>
  let promise = fetchUser(userId);
</script>

{#await promise}
  <p>Loading...</p>
{:then user}
  <p>Hello, {user.name}!</p>
{:catch error}
  <p class="error">Error: {error.message}</p>
{/await}

<!-- Shorthand: skip loading state -->
{#await promise then user}
  <p>{user.name}</p>
{/await}

<!-- Shorthand: skip to error -->
{#await promise catch error}
  <p>{error.message}</p>
{/await}
```

#### Snippets — `{#snippet}` and `{@render}`

Snippets are reusable template fragments — the Svelte 5 replacement for slots:

```svelte
<!-- Define a snippet -->
{#snippet greeting(name, formal = false)}
  {#if formal}
    <p>Good day, {name}.</p>
  {:else}
    <p>Hey {name}!</p>
  {/if}
{/snippet}

<!-- Render it -->
{@render greeting('Alice')}
{@render greeting('Dr. Smith', true)}
```

##### Passing Snippets as Props

```svelte
<!-- Table.svelte -->
<script>
  let { rows, header, row } = $props();
</script>

<table>
  <thead>
    <tr>{@render header()}</tr>
  </thead>
  <tbody>
    {#each rows as r}
      <tr>{@render row(r)}</tr>
    {/each}
  </tbody>
</table>
```

```svelte
<!-- Usage -->
<Table {rows}>
  {#snippet header()}
    <th>Name</th><th>Score</th>
  {/snippet}
  {#snippet row(r)}
    <td>{r.name}</td><td>{r.score}</td>
  {/snippet}
</Table>
```

##### Default Snippet (children)

Content between component tags becomes the `children` prop:

```svelte
<!-- Card.svelte -->
<script>
  let { children, title } = $props();
</script>

<div class="card">
  <h2>{title}</h2>
  {@render children()}
</div>
```

```svelte
<Card title="My Card">
  <p>This is the card body.</p>
</Card>
```

#### `{@const}` — Local Constants

Declare a local constant inside a template block:

```svelte
{#each boxes as box}
  {@const area = box.width * box.height}
  {@const label = area > 100 ? 'large' : 'small'}
  <p>{box.id}: {area}px² ({label})</p>
{/each}
```

#### `{@html}` — Raw HTML

```svelte
<script>
  // WARNING: Always sanitize before using @html
  import DOMPurify from 'dompurify';
  let rawHtml = $state('<p>Hello <strong>world</strong></p>');
  let safe = $derived(DOMPurify.sanitize(rawHtml));
</script>

<div>{@html safe}</div>
```

> [!CAUTION]
> `{@html}` bypasses Svelte's XSS protection. **Never** use it with unsanitized user input. Always use a library like DOMPurify to sanitize HTML before rendering.

#### `{@debug}` — Template Debugging

```svelte
<script>
  let user = $state({ name: 'Alice', role: 'admin' });
</script>

<!-- Pauses execution in devtools when user changes -->
{@debug user}
<p>{user.name}</p>
```

### 4.6 Directives

#### Event Handling

Svelte 5 uses standard HTML event attributes:

```svelte
<script>
  function handleClick(event) {
    console.log('clicked', event.target);
  }

  function handleInput(event) {
    console.log(event.target.value);
  }
</script>

<!-- Inline handler -->
<button onclick={() => count++}>+1</button>

<!-- Named handler -->
<button onclick={handleClick}>Click me</button>

<!-- With event object -->
<input oninput={handleInput} />

<!-- Multiple events -->
<div
  onmouseenter={() => hovered = true}
  onmouseleave={() => hovered = false}
>Hover me</div>
```

> [!NOTE]
> Svelte 5 uses **direct event attributes** (`onclick`, `oninput`) rather than the legacy `on:click`, `on:input` syntax. Both work during the migration period, but new code should use the native attribute form.

##### Delegated Events

Svelte automatically delegates these events for performance (one listener on the document root instead of per-element):

`beforeinput`, `click`, `change`, `dblclick`, `contextmenu`, `focusin`, `focusout`, `input`, `keydown`, `keyup`, `mousedown`, `mousemove`, `mouseout`, `mouseover`, `mouseup`, `pointerdown`, `pointermove`, `pointerout`, `pointerover`, `pointerup`, `touchend`, `touchmove`, `touchstart`

`touchstart` and `touchmove` are passive by default.

##### Event Modifiers (Legacy)

In legacy mode, `on:event|modifier` syntax is supported:

```svelte
<button on:click|preventDefault={submit}>Submit</button>
<button on:click|stopPropagation|once={action}>Once</button>
```

#### `bind:` — Two-Way Binding

```svelte
<script>
  let name = $state('');
  let checked = $state(false);
  let selected = $state('');
  let files = $state();
  let group = $state([]);
  let div;
</script>

<!-- Text input -->
<input type="text" bind:value={name} />

<!-- Checkbox -->
<input type="checkbox" bind:checked />

<!-- Select -->
<select bind:value={selected}>
  <option value="a">Option A</option>
  <option value="b">Option B</option>
</select>

<!-- File input -->
<input type="file" bind:files />

<!-- Checkbox group -->
{#each ['apple', 'banana', 'cherry'] as fruit}
  <label>
    <input type="checkbox" bind:group={group} value={fruit} />
    {fruit}
  </label>
{/each}

<!-- DOM element reference -->
<div bind:this={div}></div>

<!-- Read-only DOM bindings -->
<div bind:clientWidth={width} bind:clientHeight={height}></div>
<div bind:offsetWidth bind:offsetHeight></div>
<div bind:contentRect bind:contentBoxSize></div>
<div bind:borderBoxSize bind:devicePixelContentBoxSize></div>

<!-- Window bindings -->
<svelte:window bind:scrollX bind:scrollY />
<svelte:window bind:innerWidth bind:innerHeight />
<svelte:window bind:outerWidth bind:outerHeight />

<!-- Media bindings -->
<video
  bind:currentTime
  bind:duration
  bind:paused
  bind:volume
  bind:muted
  bind:playbackRate
  bind:buffered
  bind:seekable
  bind:played
/>

<!-- Content editable -->
<div contenteditable="true" bind:innerHTML={html}></div>
<div contenteditable="true" bind:innerText={text}></div>
```

#### `class:` — Conditional Classes

```svelte
<script>
  let active = $state(false);
  let disabled = $state(false);
</script>

<!-- Conditional class -->
<button class:active class:disabled>Button</button>

<!-- Named class different from variable -->
<div class:is-highlighted={active}>Content</div>

<!-- Class object syntax -->
<div class={{ active, disabled, 'is-large': size === 'large' }}>Content</div>

<!-- Mix static and dynamic -->
<div class="base-styles" class:active>Content</div>
```

#### `style:` — Dynamic Styles

```svelte
<script>
  let color = $state('red');
  let size = $state(16);
</script>

<!-- Single property -->
<p style:color>Red text</p>
<p style:font-size="{size}px">Sized text</p>

<!-- With importance -->
<p style:color|important={color}>Important!</p>

<!-- Object syntax via style attribute -->
<p style="color: {color}; font-size: {size}px">Mixed</p>
```

#### `use:` — Actions

Actions are functions applied to DOM elements. They receive the element and optional parameters, and can return an object with `update` and `destroy` methods:

```javascript
// actions.js
export function tooltip(element, text) {
  let tip;

  function show() {
    tip = document.createElement('div');
    tip.textContent = text;
    tip.className = 'tooltip';
    document.body.appendChild(tip);
    // position relative to element...
  }

  function hide() {
    tip?.remove();
  }

  element.addEventListener('mouseenter', show);
  element.addEventListener('mouseleave', hide);

  return {
    update(newText) {
      text = newText;
      if (tip) tip.textContent = newText;
    },
    destroy() {
      element.removeEventListener('mouseenter', show);
      element.removeEventListener('mouseleave', hide);
      tip?.remove();
    }
  };
}
```

```svelte
<script>
  import { tooltip } from './actions.js';
  let message = $state('Hello!');
</script>

<button use:tooltip={message}>Hover me</button>
```

#### `transition:`, `in:`, `out:` — Transitions

```svelte
<script>
  import { fade, fly, slide, scale, blur } from 'svelte/transition';
  let visible = $state(true);
</script>

<!-- Both in and out -->
<div transition:fade>Fades in and out</div>

<!-- Different in/out -->
<div in:fly={{ y: -20, duration: 300 }} out:fade={{ duration: 150 }}>
  Different transitions
</div>

<!-- With parameters -->
<div transition:slide={{ duration: 400, easing: cubicOut }}>
  Slides
</div>

<!-- Local transition (only runs for this component's mount/unmount) -->
<div transition:fade|local>Local only</div>

{#if visible}
  <p transition:scale>Toggle me</p>
{/if}
```

#### `animate:` — List Animations

```svelte
<script>
  import { flip } from 'svelte/animate';
  let items = $state([1, 2, 3, 4, 5]);
</script>

{#each items as item (item)}
  <div animate:flip={{ duration: 300 }}>
    {item}
  </div>
{/each}
```

### 4.7 Scoped Styles

CSS inside `<style>` is automatically scoped to the component via a generated class attribute:

```svelte
<style>
  /* Only applies to <p> elements in THIS component */
  p { color: blue; }

  /* :global() escapes scoping */
  :global(body) { margin: 0; }
  
  /* Target children of a component using :global */
  .wrapper :global(p) { font-size: 1.2rem; }

  /* CSS custom properties are inherited by children */
  .themed {
    --accent: #ff6b35;
  }
</style>
```

#### The `style` attribute on component tags

Pass CSS variables to child components:

```svelte
<!-- Parent -->
<ChildComponent style="--color: red; --size: 1.5rem" />
```

```svelte
<!-- ChildComponent.svelte -->
<p style="color: var(--color); font-size: var(--size)">
  Styled via CSS variable
</p>
```

### 4.8 Special Elements

#### `<svelte:component>` — Dynamic Components

```svelte
<script>
  import Foo from './Foo.svelte';
  import Bar from './Bar.svelte';

  let selected = $state('foo');
  let component = $derived(selected === 'foo' ? Foo : Bar);
</script>

<svelte:component this={component} prop="value" />
<!-- or simply: -->
<component this={component} prop="value" />
```

#### `<svelte:element>` — Dynamic HTML Elements

```svelte
<script>
  let tag = $state('h2');
</script>

<svelte:element this={tag} class="title">
  Dynamic heading
</svelte:element>
```

#### `<svelte:window>` — Window Bindings

```svelte
<script>
  let scrollY = $state(0);
  let innerWidth = $state(0);
</script>

<svelte:window
  bind:scrollY
  bind:innerWidth
  onresize={() => console.log('resized')}
  onkeydown={(e) => console.log(e.key)}
/>
```

#### `<svelte:document>` — Document Bindings

```svelte
<svelte:document
  bind:fullscreenElement
  bind:visibilityState
  onclick={handleClick}
/>
```

#### `<svelte:body>` — Body Element Bindings

```svelte
<svelte:body onmouseenter={() => bodyHovered = true} />
```

#### `<svelte:head>` — Document Head

```svelte
<svelte:head>
  <title>My Page Title</title>
  <meta name="description" content="Page description" />
  <link rel="stylesheet" href="/fonts.css" />
</svelte:head>
```

#### `<svelte:self>` — Recursive Components

```svelte
<!-- TreeNode.svelte -->
<script>
  let { node } = $props();
</script>

<li>
  {node.name}
  {#if node.children?.length}
    <ul>
      {#each node.children as child}
        <svelte:self node={child} />
      {/each}
    </ul>
  {/if}
</li>
```

#### `<svelte:fragment>` — Slot Targeting (Legacy)

In legacy slot mode, render content into a named slot without a wrapper element:

```svelte
<Layout>
  <svelte:fragment slot="header">
    <h1>Title</h1>
    <p>Subtitle</p>
  </svelte:fragment>
</Layout>
```

#### `<svelte:boundary>` — Error and Loading Boundaries

Catch errors or show loading states for async content:

```svelte
<svelte:boundary>
  <AsyncComponent />

  {#snippet failed(error, reset)}
    <div class="error">
      <p>Something went wrong: {error.message}</p>
      <button onclick={reset}>Try again</button>
    </div>
  {/snippet}
</svelte:boundary>
```

> [!NOTE]
> `<svelte:boundary>` with the `failed` snippet creates an error boundary similar to React's `ErrorBoundary`. When a child component throws during rendering or in an async operation, the `failed` snippet is rendered instead.

### 4.9 Stores (`svelte/store`)

Stores are observable objects that work across component boundaries and in non-component code. They remain useful in Svelte 5 alongside runes.

#### Writable Store

```javascript
// stores.js
import { writable } from 'svelte/store';

export const count = writable(0);
export const user = writable(null);
```

```svelte
<script>
  import { count } from './stores.js';

  // Auto-subscribe with $ prefix (component-only)
  // Subscription is cleaned up automatically on destroy
</script>

<!-- $-prefix auto-subscribes in templates -->
<p>{$count}</p>
<button onclick={() => count.update(n => n + 1)}>+</button>
<button onclick={() => count.set(0)}>Reset</button>
```

```javascript
// In non-component code
import { get } from 'svelte/store';
import { count } from './stores.js';

// Manual subscribe
const unsubscribe = count.subscribe(value => {
  console.log(value);
});
unsubscribe(); // clean up

// Get current value without subscribing
const current = get(count);
```

#### Readable Store

```javascript
import { readable } from 'svelte/store';

export const time = readable(new Date(), (set) => {
  const interval = setInterval(() => set(new Date()), 1000);
  return () => clearInterval(interval); // cleanup
});
```

#### Derived Store

```javascript
import { derived, readable } from 'svelte/store';

const time = readable(new Date(), /* ... */);

export const hours = derived(time, $time => $time.getHours());

// Multiple source stores
export const summary = derived(
  [firstName, lastName],
  ([$first, $last]) => `${$first} ${$last}`
);

// Async derived
export const data = derived(url, ($url, set) => {
  fetch($url)
    .then(r => r.json())
    .then(set);
});
```

#### Custom Stores

```javascript
import { writable } from 'svelte/store';

function createCart() {
  const { subscribe, set, update } = writable([]);

  return {
    subscribe,
    add: (item) => update(items => [...items, item]),
    remove: (id) => update(items => items.filter(i => i.id !== id)),
    clear: () => set([]),
    get total() {
      // Note: this doesn't work reactively as a getter on the store object
      // Use a derived store for reactive totals
    }
  };
}

export const cart = createCart();
```

#### Stores in Runes Mode

Stores and runes compose naturally:

```svelte
<script>
  import { writable } from 'svelte/store';
  import { count } from './stores.js';

  // Convert store to rune-friendly reactive value
  let countValue = $derived($count); // uses auto-subscribe

  // Or use a local store reactively
  const local = writable(0);
</script>

<p>{$local}</p>
<button onclick={() => local.update(n => n + 1)}>+</button>
```

### 4.10 Motion (`svelte/motion`)

#### `tweened` — Interpolated Values

```svelte
<script>
  import { tweened } from 'svelte/motion';
  import { cubicOut } from 'svelte/easing';

  const progress = tweened(0, {
    duration: 400,
    easing: cubicOut
  });

  function fill() {
    progress.set(1);
  }
</script>

<button onclick={fill}>Fill</button>
<progress value={$progress}></progress>
```

Tweened supports numbers, arrays, and objects with numeric values:

```javascript
const coords = tweened({ x: 0, y: 0 }, { duration: 300 });
await coords.set({ x: 100, y: 200 });
```

Options:
- `duration` — ms (or function `(from, to) => ms`)
- `easing` — easing function from `svelte/easing`
- `interpolate` — custom `(from, to) => (t) => value` function
- `delay` — ms before starting

#### `spring` — Physics-Based Animation

```svelte
<script>
  import { spring } from 'svelte/motion';

  const coords = spring({ x: 0, y: 0 }, {
    stiffness: 0.1,  // 0–1, lower = slower
    damping: 0.25,   // 0–1, lower = more oscillation
    precision: 0.01  // threshold to consider "settled"
  });

  function handleMouseMove(event) {
    coords.set({ x: event.clientX, y: event.clientY });
  }
</script>

<svelte:window onmousemove={handleMouseMove} />
<div
  style:left="{$coords.x}px"
  style:top="{$coords.y}px"
  style="position: fixed"
>
  🐼
</div>
```

You can update spring properties reactively:

```javascript
coords.stiffness = 0.3;
coords.damping = 0.5;
```

### 4.11 Transitions (`svelte/transition`)

#### `fade`

```svelte
<script>
  import { fade } from 'svelte/transition';
</script>

<div transition:fade={{ duration: 300 }}>Fades</div>
```

Parameters: `delay`, `duration`, `easing`

#### `blur`

```svelte
<div transition:blur={{ amount: 10, duration: 400 }}>Blurs</div>
```

Parameters: `delay`, `duration`, `easing`, `opacity`, `amount`

#### `fly`

```svelte
<div transition:fly={{ x: 0, y: -50, duration: 400 }}>Flies in from above</div>
```

Parameters: `delay`, `duration`, `easing`, `x`, `y`, `opacity`

#### `slide`

```svelte
<div transition:slide={{ duration: 300, axis: 'y' }}>
  Slides open/close
</div>
```

Parameters: `delay`, `duration`, `easing`, `axis` (`'x'` or `'y'`)

#### `scale`

```svelte
<div transition:scale={{ start: 0.5, opacity: 0, duration: 300 }}>
  Scales in
</div>
```

Parameters: `delay`, `duration`, `easing`, `start`, `opacity`

#### `draw` (SVG only)

```svelte
<svg>
  <path
    transition:draw={{ duration: 1000, speed: 0.5 }}
    d="M0,0 L100,100"
  />
</svg>
```

Parameters: `delay`, `speed`, `duration`, `easing`

#### `crossfade` — Shared Element Transitions

```svelte
<script>
  import { crossfade } from 'svelte/transition';
  import { quintOut } from 'svelte/easing';

  const [send, receive] = crossfade({
    duration: (d) => Math.sqrt(d * 200),
    fallback: fly
  });
</script>

<!-- List A -->
{#each todoItems as item (item.id)}
  <li in:receive={{ key: item.id }} out:send={{ key: item.id }}>
    {item.text}
  </li>
{/each}

<!-- List B (done items) -->
{#each doneItems as item (item.id)}
  <li in:receive={{ key: item.id }} out:send={{ key: item.id }}>
    ✓ {item.text}
  </li>
{/each}
```

#### Custom Transitions

```javascript
// Custom transition function
function typewriter(node, { speed = 1 }) {
  const valid = node.childNodes.length === 1 && node.childNodes[0].nodeType === Node.TEXT_NODE;
  if (!valid) {
    throw new Error('This transition only works on elements with a single text node');
  }

  const text = node.textContent;
  const duration = text.length / (speed * 0.01);

  return {
    duration,
    tick: (t) => {
      const i = Math.trunc(text.length * t);
      node.textContent = text.slice(0, i);
    }
  };
}
```

```svelte
<p transition:typewriter={{ speed: 2 }}>
  Hello, world!
</p>
```

#### Transition Events

```svelte
<div
  transition:fade
  onintrostart={() => console.log('intro started')}
  onintroend={() => console.log('intro ended')}
  onoutrostart={() => console.log('outro started')}
  onoutroend={() => console.log('outro ended')}
>
  Content
</div>
```

### 4.12 Animation (`svelte/animate`)

#### `flip` — First, Last, Invert, Play

FLIP animations automatically animate list items when they reorder:

```svelte
<script>
  import { flip } from 'svelte/animate';
  import { quintOut } from 'svelte/easing';

  let items = $state(['Alice', 'Bob', 'Charlie', 'Dave']);

  function shuffle() {
    items = [...items].sort(() => Math.random() - 0.5);
  }
</script>

<button onclick={shuffle}>Shuffle</button>

<ul>
  {#each items as item (item)}
    <li animate:flip={{ duration: 400, easing: quintOut }}>
      {item}
    </li>
  {/each}
</ul>
```

Parameters: `delay`, `duration`, `easing`

### 4.13 Easing Functions (`svelte/easing`)

All standard easing functions are available:

| Function | Description |
|----------|-------------|
| `linear` | Constant speed |
| `quadIn`, `quadOut`, `quadInOut` | Quadratic |
| `cubicIn`, `cubicOut`, `cubicInOut` | Cubic |
| `quartIn`, `quartOut`, `quartInOut` | Quartic |
| `quintIn`, `quintOut`, `quintInOut` | Quintic |
| `sineIn`, `sineOut`, `sineInOut` | Sinusoidal |
| `expoIn`, `expoOut`, `expoInOut` | Exponential |
| `circIn`, `circOut`, `circInOut` | Circular |
| `backIn`, `backOut`, `backInOut` | Overshoots slightly |
| `bounceIn`, `bounceOut`, `bounceInOut` | Bounces |
| `elasticIn`, `elasticOut`, `elasticInOut` | Spring-like wobble |

```svelte
<script>
  import { fade } from 'svelte/transition';
  import { elasticOut, bounceOut } from 'svelte/easing';
</script>

<div transition:fade={{ easing: elasticOut, duration: 600 }}>
  Elastic fade
</div>
```

### 4.14 Reactive Utilities (`svelte/reactivity`)

Reactive wrappers for built-in JavaScript objects:

```svelte
<script>
  import {
    SvelteDate,
    SvelteMap,
    SvelteSet,
    SvelteURL,
    SvelteURLSearchParams
  } from 'svelte/reactivity';

  // Reactive Date — updates when methods mutate the date
  const now = new SvelteDate();
  setInterval(() => now.setMilliseconds(Date.now()), 1000);

  // Reactive Map — updates on set/delete/clear
  const map = new SvelteMap([['key', 'value']]);
  map.set('another', 'item');

  // Reactive Set — updates on add/delete/clear
  const set = new SvelteSet([1, 2, 3]);
  set.add(4);

  // Reactive URL
  const url = new SvelteURL('https://example.com/path?q=hello');
  url.searchParams.set('page', '2'); // triggers update
</script>

<p>Now: {now.toLocaleTimeString()}</p>
<p>Map size: {map.size}</p>
<p>URL: {url.href}</p>
```

#### Window Reactive Values (`svelte/reactivity/window`)

```svelte
<script>
  import {
    innerWidth,
    innerHeight,
    outerWidth,
    outerHeight,
    scrollX,
    scrollY,
    devicePixelRatio
  } from 'svelte/reactivity/window';
</script>

<p>
  Window: {$innerWidth} × {$innerHeight}
  (DPR: {$devicePixelRatio})
</p>
```

### 4.15 Rendering API

#### `mount` — Client-Side Rendering

```javascript
import { mount } from 'svelte';
import App from './App.svelte';

const app = mount(App, {
  target: document.getElementById('app'), // required
  props: { name: 'world' },              // optional
  anchor: null,                           // insert before this element
  context: new Map([['key', 'value']]),  // initial context
  intro: false                            // animate on initial render
});
```

#### `unmount` — Remove a Component

```javascript
import { unmount } from 'svelte';

// Later, when done:
unmount(app);
```

#### `hydrate` — SSR Hydration

```javascript
import { hydrate } from 'svelte';
import App from './App.svelte';

// Attaches event listeners to server-rendered HTML
const app = hydrate(App, {
  target: document.getElementById('app'),
  props: { /* must match SSR props */ }
});
```

### 4.16 Utilities

#### `tick()` — Await DOM Updates

```svelte
<script>
  import { tick } from 'svelte';

  let messages = $state([]);
  let container;

  async function addMessage(text) {
    messages.push(text);
    await tick(); // Wait for DOM to update
    container.scrollTop = container.scrollHeight;
  }
</script>
```

#### `untrack()` — Opt Out of Tracking

```svelte
<script>
  import { untrack } from 'svelte';

  let count = $state(0);
  let threshold = $state(10);

  $effect(() => {
    // This effect only tracks `count`, not `threshold`
    if (count > untrack(() => threshold)) {
      console.log('Count exceeded threshold!');
    }
  });
</script>
```

#### `flushSync()` — Synchronous State Flush

Force all pending state updates to apply synchronously (useful for testing):

```javascript
import { flushSync } from 'svelte';

count++;
flushSync(); // DOM is now updated
expect(document.querySelector('p').textContent).toBe('1');
```

#### `fork()` — Off-Screen Preloading (since 5.42)

Create a detached copy of the current reactive context for preloading:

```javascript
import { fork, mount, unmount } from 'svelte';

const branch = fork();
// Run computations without affecting the main tree
const result = await branch.run(() => expensiveComputation());
```

#### `settled()` — Await All Async Work

```javascript
import { settled } from 'svelte';

// After triggering async updates
await settled();
// All async operations and state updates are now complete
```

#### `getAbortSignal()` — Reactive AbortSignal

Returns an `AbortSignal` that aborts when the current derived value or effect re-runs — perfect for cancelling fetch requests:

```svelte
<script>
  import { getAbortSignal } from 'svelte';

  let userId = $state(1);

  let userData = $derived.by(async () => {
    const signal = getAbortSignal();
    const response = await fetch(`/api/users/${userId}`, { signal });
    return response.json();
  });
</script>
```

---

## 5. Common Tasks

### 5.1 Building a Form

```svelte
<script>
  let form = $state({
    name: '',
    email: '',
    message: '',
    agree: false
  });

  let errors = $state({});
  let submitting = $state(false);
  let submitted = $state(false);

  function validate() {
    const e = {};
    if (!form.name.trim()) e.name = 'Name is required';
    if (!form.email.includes('@')) e.email = 'Valid email required';
    if (form.message.length < 10) e.message = 'Message must be at least 10 characters';
    if (!form.agree) e.agree = 'You must agree to the terms';
    return e;
  }

  async function handleSubmit(event) {
    event.preventDefault();
    errors = validate();
    if (Object.keys(errors).length > 0) return;

    submitting = true;
    try {
      await fetch('/api/contact', {
        method: 'POST',
        headers: { 'Content-Type': 'application/json' },
        body: JSON.stringify($state.snapshot(form))
      });
      submitted = true;
    } finally {
      submitting = false;
    }
  }
</script>

{#if submitted}
  <p class="success">Thank you! We'll be in touch.</p>
{:else}
  <form onsubmit={handleSubmit}>
    <div>
      <label for="name">Name</label>
      <input id="name" bind:value={form.name} />
      {#if errors.name}<span class="error">{errors.name}</span>{/if}
    </div>

    <div>
      <label for="email">Email</label>
      <input id="email" type="email" bind:value={form.email} />
      {#if errors.email}<span class="error">{errors.email}</span>{/if}
    </div>

    <div>
      <label for="message">Message</label>
      <textarea id="message" bind:value={form.message}></textarea>
      {#if errors.message}<span class="error">{errors.message}</span>{/if}
    </div>

    <div>
      <label>
        <input type="checkbox" bind:checked={form.agree} />
        I agree to the terms
      </label>
      {#if errors.agree}<span class="error">{errors.agree}</span>{/if}
    </div>

    <button type="submit" disabled={submitting}>
      {submitting ? 'Sending...' : 'Send'}
    </button>
  </form>
{/if}
```

### 5.2 Fetching Data

```svelte
<script>
  let userId = $state(1);
  let userPromise = $derived(fetchUser(userId));

  async function fetchUser(id) {
    const res = await fetch(`https://jsonplaceholder.typicode.com/users/${id}`);
    if (!res.ok) throw new Error(`HTTP ${res.status}`);
    return res.json();
  }
</script>

<label>
  User ID:
  <input type="number" bind:value={userId} min="1" max="10" />
</label>

{#await userPromise}
  <p>Loading user {userId}...</p>
{:then user}
  <h2>{user.name}</h2>
  <p>{user.email}</p>
{:catch error}
  <p class="error">Failed: {error.message}</p>
{/await}
```

#### Fetch with Cancellation

```svelte
<script>
  import { getAbortSignal } from 'svelte';

  let query = $state('');

  let results = $derived.by(async () => {
    if (!query) return [];
    const signal = getAbortSignal();
    const res = await fetch(`/api/search?q=${encodeURIComponent(query)}`, { signal });
    return res.json();
  });
</script>

<input bind:value={query} placeholder="Search..." />

{#await results}
  <p>Searching...</p>
{:then items}
  {#each items as item}
    <div>{item.title}</div>
  {/each}
{/await}
```

### 5.3 Global State with Runes (`.svelte.js` modules)

Create shared reactive state that works across components:

```javascript
// state/cart.svelte.js
let items = $state([]);
let discount = $state(0);

const total = $derived(
  items.reduce((sum, item) => sum + item.price * item.qty, 0) * (1 - discount)
);

export function useCart() {
  return {
    get items() { return items; },
    get total() { return total; },
    addItem(item) {
      const existing = items.find(i => i.id === item.id);
      if (existing) existing.qty++;
      else items.push({ ...item, qty: 1 });
    },
    removeItem(id) {
      items = items.filter(i => i.id !== id);
    },
    applyDiscount(pct) {
      discount = pct / 100;
    },
    clear() {
      items = [];
      discount = 0;
    }
  };
}
```

```svelte
<!-- CartIcon.svelte -->
<script>
  import { useCart } from '../state/cart.svelte.js';
  const cart = useCart();
</script>

<span>{cart.items.length} items</span>
<span>${cart.total.toFixed(2)}</span>
```

### 5.4 Modal Dialog

```svelte
<!-- Modal.svelte -->
<script>
  let { open = $bindable(false), title, children } = $props();
  import { fade, scale } from 'svelte/transition';

  function close() { open = false; }

  function handleKeydown(e) {
    if (e.key === 'Escape') close();
  }
</script>

<svelte:window onkeydown={handleKeydown} />

{#if open}
  <!-- Backdrop -->
  <div
    class="overlay"
    transition:fade={{ duration: 150 }}
    onclick={close}
    role="presentation"
  ></div>

  <!-- Dialog -->
  <div
    class="modal"
    transition:scale={{ start: 0.95, duration: 150 }}
    role="dialog"
    aria-modal="true"
    aria-labelledby="modal-title"
  >
    <div class="modal-header">
      <h2 id="modal-title">{title}</h2>
      <button onclick={close} aria-label="Close">&times;</button>
    </div>
    <div class="modal-body">
      {@render children()}
    </div>
  </div>
{/if}

<style>
  .overlay {
    position: fixed;
    inset: 0;
    background: rgba(0,0,0,0.5);
    z-index: 100;
  }
  .modal {
    position: fixed;
    top: 50%;
    left: 50%;
    transform: translate(-50%, -50%);
    background: white;
    border-radius: 8px;
    padding: 1.5rem;
    z-index: 101;
    min-width: 400px;
    max-width: 90vw;
  }
</style>
```

Usage:

```svelte
<script>
  import Modal from './Modal.svelte';
  let showModal = $state(false);
</script>

<button onclick={() => showModal = true}>Open Modal</button>

<Modal bind:open={showModal} title="Confirm Action">
  <p>Are you sure you want to proceed?</p>
  <button onclick={() => showModal = false}>Cancel</button>
  <button onclick={confirmAction}>Confirm</button>
</Modal>
```

### 5.5 Toast Notifications

```javascript
// toast.svelte.js
let toasts = $state([]);
let nextId = 0;

export function useToasts() {
  return {
    get list() { return toasts; },
    add(message, type = 'info', duration = 3000) {
      const id = ++nextId;
      toasts.push({ id, message, type });
      if (duration > 0) setTimeout(() => this.remove(id), duration);
      return id;
    },
    remove(id) {
      toasts = toasts.filter(t => t.id !== id);
    }
  };
}
```

```svelte
<!-- ToastContainer.svelte -->
<script>
  import { useToasts } from './toast.svelte.js';
  import { fly } from 'svelte/transition';
  import { flip } from 'svelte/animate';

  const toasts = useToasts();
</script>

<div class="toast-container">
  {#each toasts.list as toast (toast.id)}
    <div
      class="toast {toast.type}"
      animate:flip={{ duration: 200 }}
      in:fly={{ x: 300, duration: 300 }}
      out:fly={{ x: 300, duration: 200 }}
    >
      <span>{toast.message}</span>
      <button onclick={() => toasts.remove(toast.id)}>&times;</button>
    </div>
  {/each}
</div>
```

### 5.6 Infinite Scroll

```svelte
<script>
  import { onMount } from 'svelte';

  let items = $state([]);
  let page = $state(0);
  let loading = $state(false);
  let hasMore = $state(true);
  let sentinel;

  async function loadMore() {
    if (loading || !hasMore) return;
    loading = true;
    try {
      const res = await fetch(`/api/items?page=${page}`);
      const data = await res.json();
      items.push(...data.items);
      hasMore = data.hasMore;
      page++;
    } finally {
      loading = false;
    }
  }

  onMount(() => {
    loadMore(); // Initial load

    const observer = new IntersectionObserver(
      (entries) => { if (entries[0].isIntersecting) loadMore(); },
      { threshold: 0.1 }
    );

    if (sentinel) observer.observe(sentinel);
    return () => observer.disconnect();
  });
</script>

{#each items as item (item.id)}
  <div class="item">{item.title}</div>
{/each}

<div bind:this={sentinel}>
  {#if loading}
    <p>Loading...</p>
  {:else if !hasMore}
    <p>All items loaded.</p>
  {/if}
</div>
```

### 5.7 Drag and Drop

```svelte
<script>
  let items = $state(['Alpha', 'Beta', 'Gamma', 'Delta']);
  let dragging = $state(null);

  function dragstart(event, index) {
    dragging = index;
    event.dataTransfer.effectAllowed = 'move';
  }

  function dragover(event, index) {
    event.preventDefault();
    if (dragging === null || dragging === index) return;

    const newItems = [...items];
    const [removed] = newItems.splice(dragging, 1);
    newItems.splice(index, 0, removed);
    items = newItems;
    dragging = index;
  }

  function dragend() {
    dragging = null;
  }
</script>

<ul>
  {#each items as item, i (item)}
    <li
      draggable="true"
      class:dragging={dragging === i}
      ondragstart={(e) => dragstart(e, i)}
      ondragover={(e) => dragover(e, i)}
      ondragend={dragend}
    >
      {item}
    </li>
  {/each}
</ul>
```

### 5.8 Tabbed Interface

```svelte
<script>
  let tabs = $state([
    { id: 'overview', label: 'Overview' },
    { id: 'details', label: 'Details' },
    { id: 'history', label: 'History' }
  ]);
  let activeTab = $state('overview');
</script>

<div role="tablist">
  {#each tabs as tab}
    <button
      role="tab"
      aria-selected={activeTab === tab.id}
      aria-controls="panel-{tab.id}"
      onclick={() => activeTab = tab.id}
    >
      {tab.label}
    </button>
  {/each}
</div>

{#each tabs as tab}
  <div
    role="tabpanel"
    id="panel-{tab.id}"
    hidden={activeTab !== tab.id}
  >
    {#if tab.id === 'overview'}
      <p>Overview content...</p>
    {:else if tab.id === 'details'}
      <p>Details content...</p>
    {:else}
      <p>History content...</p>
    {/if}
  </div>
{/each}
```

### 5.9 Debounced Input

```svelte
<script>
  let query = $state('');
  let debouncedQuery = $state('');
  let timer;

  $effect(() => {
    clearTimeout(timer);
    timer = setTimeout(() => {
      debouncedQuery = query;
    }, 300);

    return () => clearTimeout(timer);
  });

  let results = $derived(debouncedQuery ? search(debouncedQuery) : []);
</script>

<input bind:value={query} placeholder="Type to search..." />
<p>Searching for: {debouncedQuery}</p>
```

### 5.10 Accordion Component

```svelte
<!-- Accordion.svelte -->
<script>
  let { items } = $props();
  let openIndex = $state(null);

  function toggle(i) {
    openIndex = openIndex === i ? null : i;
  }
</script>

{#each items as item, i}
  <div class="accordion-item">
    <button
      class="accordion-header"
      aria-expanded={openIndex === i}
      onclick={() => toggle(i)}
    >
      {item.title}
      <span class="icon">{openIndex === i ? '−' : '+'}</span>
    </button>

    {#if openIndex === i}
      <div
        class="accordion-body"
        transition:slide={{ duration: 200 }}
      >
        {item.body}
      </div>
    {/if}
  </div>
{/each}
```

### 5.11 Virtual List (Windowing)

For very large lists, render only visible items:

```svelte
<script>
  let items = $state(Array.from({ length: 10000 }, (_, i) => ({ id: i, text: `Item ${i}` })));
  const ITEM_HEIGHT = 40;
  const VISIBLE_COUNT = 20;
  const BUFFER = 5;

  let scrollTop = $state(0);
  let containerHeight = $state(800);

  let visibleStart = $derived(Math.max(0, Math.floor(scrollTop / ITEM_HEIGHT) - BUFFER));
  let visibleEnd = $derived(Math.min(items.length, visibleStart + VISIBLE_COUNT + BUFFER * 2));
  let visibleItems = $derived(items.slice(visibleStart, visibleEnd));
  let totalHeight = $derived(items.length * ITEM_HEIGHT);
  let offsetY = $derived(visibleStart * ITEM_HEIGHT);
</script>

<div
  style:height="{containerHeight}px"
  style="overflow-y: auto"
  onscroll={(e) => scrollTop = e.target.scrollTop}
>
  <div style:height="{totalHeight}px" style="position: relative">
    <div style:transform="translateY({offsetY}px)">
      {#each visibleItems as item (item.id)}
        <div style:height="{ITEM_HEIGHT}px">{item.text}</div>
      {/each}
    </div>
  </div>
</div>
```

### 5.12 Custom Store Pattern — User Session

```javascript
// session.svelte.js
import { writable, derived, get } from 'svelte/store';

const _user = writable(null);
const _token = writable(localStorage.getItem('token'));

export const user = { subscribe: _user.subscribe };
export const isLoggedIn = derived(_user, $u => $u !== null);

export async function login(email, password) {
  const res = await fetch('/api/auth/login', {
    method: 'POST',
    headers: { 'Content-Type': 'application/json' },
    body: JSON.stringify({ email, password })
  });
  if (!res.ok) throw new Error('Login failed');
  const { user: userData, token } = await res.json();
  _user.set(userData);
  _token.set(token);
  localStorage.setItem('token', token);
}

export function logout() {
  _user.set(null);
  _token.set(null);
  localStorage.removeItem('token');
}

export function getAuthHeader() {
  const token = get(_token);
  return token ? { Authorization: `Bearer ${token}` } : {};
}
```

### 5.13 Routing with SvelteKit

SvelteKit uses a file-based router. Files in `src/routes/` define pages:

```
src/routes/
  +page.svelte          → /
  +layout.svelte        → shared layout wrapper
  about/
    +page.svelte        → /about
  blog/
    +page.svelte        → /blog
    +page.ts            → data loading for /blog
    [slug]/
      +page.svelte      → /blog/my-post
      +page.ts          → data loading with slug param
  api/
    users/
      +server.ts        → /api/users (REST endpoint)
```

Page with data loading:

```typescript
// src/routes/blog/[slug]/+page.ts
import type { PageLoad } from './$types';

export const load: PageLoad = async ({ params, fetch }) => {
  const post = await fetch(`/api/posts/${params.slug}`).then(r => r.json());
  return { post };
};
```

```svelte
<!-- src/routes/blog/[slug]/+page.svelte -->
<script>
  let { data } = $props();
</script>

<article>
  <h1>{data.post.title}</h1>
  <div>{@html data.post.content}</div>
</article>
```

### 5.14 Reactive Class (`$state` + class pattern)

```javascript
// Counter.svelte.js
export class Counter {
  count = $state(0);
  step = $state(1);
  history = $state([]);

  doubled = $derived(this.count * 2);

  increment() {
    this.count += this.step;
    this.history.push({ action: 'increment', value: this.count });
  }

  reset() {
    this.count = 0;
    this.history = [];
  }
}
```

```svelte
<script>
  import { Counter } from './Counter.svelte.js';
  const counter = new Counter();
</script>

<button onclick={() => counter.increment()}>+{counter.step}</button>
<p>Count: {counter.count} (Doubled: {counter.doubled})</p>
```

### 5.15 Server-Side Rendering (SSR)

For standalone Svelte (not SvelteKit), use `svelte/server`:

```javascript
// server.js
import { render } from 'svelte/server';
import App from './App.svelte';

const { head, body } = render(App, {
  props: { name: 'World' }
});

const html = `<!DOCTYPE html>
<html>
  <head>${head}</head>
  <body>${body}</body>
</html>`;
```

Then hydrate on the client:

```javascript
// client.js
import { hydrate } from 'svelte';
import App from './App.svelte';

hydrate(App, {
  target: document.body,
  props: { name: 'World' }
});
```

### 5.16 Custom Elements (Web Components)

```svelte
<svelte:options customElement="my-greeting" />

<script>
  let { name = 'World' } = $props();
</script>

<h1>Hello, {name}!</h1>

<style>
  h1 { color: navy; }
</style>
```

Build it:

```javascript
// vite.config.js
import { svelte } from '@sveltejs/vite-plugin-svelte';

export default {
  plugins: [svelte({
    compilerOptions: { customElement: true }
  })],
  build: {
    lib: {
      entry: 'src/my-greeting.svelte',
      formats: ['es']
    }
  }
};
```

Usage in plain HTML:

```html
<script type="module" src="./my-greeting.js"></script>
<my-greeting name="Alice"></my-greeting>
```

Custom element with advanced options:

```svelte
<svelte:options customElement={{
  tag: 'my-widget',
  shadow: 'none',           // 'open' | 'none'
  props: {
    name: { type: String },
    count: { type: Number, attribute: 'data-count' }
  }
}} />
```

### 5.17 `{#snippet}` Advanced Patterns

#### Snippets as Layout Components

```svelte
<!-- DataTable.svelte -->
<script>
  let { data, columns, emptyMessage = 'No data', actions } = $props();
</script>

<table>
  <thead>
    <tr>
      {#each columns as col}
        <th>{col.label}</th>
      {/each}
      {#if actions}<th>Actions</th>{/if}
    </tr>
  </thead>
  <tbody>
    {#each data as row (row.id)}
      <tr>
        {#each columns as col}
          <td>{row[col.key]}</td>
        {/each}
        {#if actions}
          <td>{@render actions(row)}</td>
        {/if}
      </tr>
    {:else}
      <tr><td colspan={columns.length + (actions ? 1 : 0)}>{emptyMessage}</td></tr>
    {/each}
  </tbody>
</table>
```

```svelte
<DataTable {data} {columns}>
  {#snippet actions(row)}
    <button onclick={() => edit(row)}>Edit</button>
    <button onclick={() => deleteRow(row.id)}>Delete</button>
  {/snippet}
</DataTable>
```

### 5.18 Transition Recipe: Page Transitions

```svelte
<!-- +layout.svelte (SvelteKit) -->
<script>
  import { fade } from 'svelte/transition';
  let { children } = $props();
</script>

{#key page.url.pathname}
  <div transition:fade={{ duration: 200 }}>
    {@render children()}
  </div>
{/key}
```

### 5.19 Binding to Media Elements

```svelte
<script>
  let paused = $state(true);
  let currentTime = $state(0);
  let duration = $state(0);
  let volume = $state(1);
  let video;

  let progress = $derived(duration ? currentTime / duration : 0);
</script>

<video
  bind:this={video}
  bind:paused
  bind:currentTime
  bind:duration
  bind:volume
  src="/video.mp4"
></video>

<div class="controls">
  <button onclick={() => paused = !paused}>
    {paused ? '▶' : '⏸'}
  </button>
  <input
    type="range" min="0" max="1" step="0.01"
    bind:value={progress}
    oninput={(e) => currentTime = e.target.value * duration}
  />
  <span>{Math.floor(currentTime)}s / {Math.floor(duration)}s</span>
</div>
```

### 5.20 Keyboard Shortcuts

```svelte
<script>
  function handleKeydown(event) {
    if (event.ctrlKey && event.key === 's') {
      event.preventDefault();
      save();
    }
    if (event.key === 'Escape') {
      closeModal();
    }
    if (event.key === '?' && !event.target.closest('input, textarea')) {
      showHelp = true;
    }
  }
</script>

<svelte:window onkeydown={handleKeydown} />
```

### 5.21 Clipboard Operations

```svelte
<script>
  let copied = $state(false);

  async function copyToClipboard(text) {
    try {
      await navigator.clipboard.writeText(text);
      copied = true;
      setTimeout(() => copied = false, 2000);
    } catch {
      // Fallback for older browsers
      const el = document.createElement('textarea');
      el.value = text;
      document.body.appendChild(el);
      el.select();
      document.execCommand('copy');
      el.remove();
    }
  }
</script>

<button onclick={() => copyToClipboard('Hello!')}>
  {copied ? '✓ Copied!' : 'Copy'}
</button>
```

### 5.22 Dark Mode with Context

```javascript
// theme.svelte.js
const THEME_KEY = Symbol('theme');

export function createTheme() {
  const prefersDark = window.matchMedia('(prefers-color-scheme: dark)').matches;
  let dark = $state(prefersDark);

  return {
    get dark() { return dark; },
    toggle() { dark = !dark; },
    setDark(value) { dark = value; }
  };
}
```

```svelte
<!-- App.svelte -->
<script>
  import { setContext } from 'svelte';
  import { createTheme } from './theme.svelte.js';

  const THEME_KEY = Symbol('theme');
  const theme = createTheme();
  setContext(THEME_KEY, theme);
</script>

<div class:dark={theme.dark} class="app">
  <!-- children -->
</div>
```

### 5.23 Intersection Observer Action

```javascript
// actions/observe.js
export function observe(element, { callback, options = {} } = {}) {
  const observer = new IntersectionObserver(([entry]) => {
    callback(entry.isIntersecting, entry);
  }, options);

  observer.observe(element);

  return {
    update({ callback: newCallback }) {
      callback = newCallback;
    },
    destroy() {
      observer.disconnect();
    }
  };
}
```

```svelte
<script>
  import { observe } from './actions/observe.js';
  let visible = $state(false);
</script>

<div
  use:observe={{ callback: (isVisible) => visible = isVisible }}
  class:visible
>
  {visible ? 'I am visible!' : 'Scroll to see me'}
</div>
```

### 5.24 Persistent State (localStorage)

```javascript
// persisted.svelte.js
export function persisted(key, initialValue) {
  let stored;
  try {
    const raw = localStorage.getItem(key);
    stored = raw ? JSON.parse(raw) : initialValue;
  } catch {
    stored = initialValue;
  }

  let value = $state(stored);

  $effect(() => {
    try {
      localStorage.setItem(key, JSON.stringify(value));
    } catch {
      // Storage full or unavailable
    }
  });

  return {
    get value() { return value; },
    set value(v) { value = v; }
  };
}
```

```svelte
<script>
  import { persisted } from './persisted.svelte.js';
  const theme = persisted('theme', 'light');
</script>

<button onclick={() => theme.value = theme.value === 'light' ? 'dark' : 'light'}>
  Current: {theme.value}
</button>
```

### 5.25 Composable Reactive Logic (Custom Hooks)

```javascript
// hooks/useWindowSize.svelte.js
export function useWindowSize() {
  let width = $state(window.innerWidth);
  let height = $state(window.innerHeight);

  function update() {
    width = window.innerWidth;
    height = window.innerHeight;
  }

  $effect(() => {
    window.addEventListener('resize', update);
    return () => window.removeEventListener('resize', update);
  });

  return {
    get width() { return width; },
    get height() { return height; }
  };
}
```

```svelte
<script>
  import { useWindowSize } from './hooks/useWindowSize.svelte.js';
  const size = useWindowSize();
</script>

<p>Window: {size.width} × {size.height}</p>
```

### 5.26 Animation Recipe: Staggered List Entry

```svelte
<script>
  import { fly } from 'svelte/transition';
  import { quintOut } from 'svelte/easing';

  let items = $state([]);

  async function loadItems() {
    items = await fetchItems();
  }
</script>

{#each items as item, i (item.id)}
  <div
    in:fly={{
      y: 20,
      duration: 300,
      delay: i * 50, // stagger by 50ms per item
      easing: quintOut
    }}
  >
    {item.name}
  </div>
{/each}
```

### 5.27 Optimistic UI Updates

```svelte
<script>
  let todos = $state([]);

  async function toggleTodo(id) {
    // Optimistically update
    const todo = todos.find(t => t.id === id);
    const previous = todo.done;
    todo.done = !todo.done;

    try {
      await fetch(`/api/todos/${id}`, {
        method: 'PATCH',
        headers: { 'Content-Type': 'application/json' },
        body: JSON.stringify({ done: todo.done })
      });
    } catch {
      // Revert on failure
      todo.done = previous;
    }
  }
</script>

{#each todos as todo (todo.id)}
  <label>
    <input type="checkbox" checked={todo.done} onchange={() => toggleTodo(todo.id)} />
    {todo.text}
  </label>
{/each}
```

### 5.28 Accessibility: Focus Management

```svelte
<script>
  import { tick } from 'svelte';

  let dialogOpen = $state(false);
  let dialog;
  let trigger;

  async function openDialog() {
    dialogOpen = true;
    await tick();
    // Move focus to dialog
    dialog?.focus();
  }

  async function closeDialog() {
    dialogOpen = false;
    await tick();
    // Return focus to trigger
    trigger?.focus();
  }
</script>

<button bind:this={trigger} onclick={openDialog}>Open</button>

{#if dialogOpen}
  <div
    role="dialog"
    aria-modal="true"
    tabindex="-1"
    bind:this={dialog}
    onkeydown={(e) => e.key === 'Escape' && closeDialog()}
  >
    <button onclick={closeDialog}>Close</button>
  </div>
{/if}
```

---

## 6. Troubleshooting

### Error: `lifecycle_outside_component`

**Cause:** Calling `onMount`, `onDestroy`, `setContext`, or `getContext` outside of a component's initialization phase. Common in SSR bundlers like Rollup/Rolldown (issue #17027).

**Solution:**
1. Ensure lifecycle calls are at the top level of a `<script>` block, not inside callbacks or async functions.
2. For SSR with Rollup/Rolldown, verify you are not accidentally calling component code in a non-component context.
3. Use `$effect.root()` for effects that need to run outside component lifecycle.

**Prevention:** Only call lifecycle functions synchronously during component initialization, never inside `setTimeout`, `Promise.then`, or event handlers.

---

### Error: `Cannot read properties of undefined (reading 'subscribe')`

**Cause:** Using the `$store` auto-subscribe syntax on a value that is not a store (not an object with a `subscribe` method), or importing a store before it is initialized.

**Solution:**
1. Verify the imported value is actually a Svelte store (has `.subscribe`).
2. Check for circular imports that may result in `undefined` at import time.
3. Use optional chaining: `$store?.property` during initialization.

**Prevention:** Export stores from a dedicated module without circular dependencies.

---

### Error: `derived_inert` warning

**Cause:** A `$derived` value exists but is never read, making it inert (issue #18097). The compiler warns because derived computations that are never consumed may indicate a logic bug.

**Solution:**
1. Read the derived value in the template or in an `$effect`.
2. If you only need the side effect, use `$effect` directly instead of `$derived`.
3. To suppress: use `void derived_value` in an `$effect` (uncommon pattern).

**Prevention:** Avoid creating derived values that are only needed for side effects.

---

### Error: `Cannot use 'bind:value' on a non-bindable prop`

**Cause:** Attempting to use `bind:value` on a child component's prop that was not declared with `$bindable()`.

**Solution:**
1. In the child component, change `let { value } = $props()` to `let { value = $bindable() } = $props()`.
2. Alternatively, pass a callback prop: `onchange={(v) => parentValue = v}`.

**Prevention:** Always mark props that should support two-way binding with `$bindable()`.

---

### Warning: `ownership_invalid_mutation`

**Cause:** A child component is directly mutating a prop value passed from a parent without `$bindable`. Svelte detects this because it violates unidirectional data flow.

**Solution:**
1. Use `$bindable()` if two-way binding is intended.
2. Use callback props to notify the parent, which updates its own state.
3. Clone the prop locally: `let local = $state($state.snapshot(propValue))`.

**Prevention:** Treat props as read-only unless explicitly marked `$bindable`.

---

### Warning: `ownership_invalid_binding`

**Cause:** Binding to a prop of a deeply nested child that does not own that state.

**Solution:**
1. Lift state up to the nearest common ancestor.
2. Use context API to share mutable state across the tree.
3. Use a shared `.svelte.js` module for cross-tree state.

---

### Error: `state_snapshot_uncloneable`

**Cause:** Calling `$state.snapshot()` on a value that contains non-cloneable items (functions, DOM elements, circular references).

**Solution:**
1. Only snapshot plain data — no functions, no DOM nodes, no class instances with methods.
2. Use `JSON.parse(JSON.stringify(value))` as a manual fallback with awareness of its limitations.

**Prevention:** Keep state values serializable if you need snapshots.

---

### `$effect` runs too often / infinite loop

**Cause:** An `$effect` reads and then writes to the same reactive state, creating a cycle. For example: reading `items` and then calling `items.push(...)` inside the same effect.

**Solution:**
1. Use `untrack(() => items)` to read without registering a dependency.
2. Separate the read and write into different effects or into event handlers.
3. Use `$derived` for derived values instead of writing back to state.

**Prevention:** Effects should perform side effects (DOM updates, fetch, logging), not compute new state values.

---

### Memory leak: `$effect` subscribing to parent prop (issue #18103)

**Cause:** In Svelte 5.55.4, an `$effect` in a child component that subscribes to a prop passed from a parent can leak the effect's cleanup if the component is destroyed and re-created frequently.

**Solution:**
1. Ensure the cleanup function returned from the `$effect` releases all references.
2. Use `onDestroy` in addition to the cleanup return for critical cleanup.
3. Monitor with browser devtools heap snapshots.

**Prevention:** Always return a cleanup function from `$effect` when registering external listeners.

---

### Memory leak: Detached DOM nodes on unresolved promises (issue #18094)

**Cause:** `{#await}` blocks with pending promises hold references to detached DOM nodes if the component is destroyed before the promise settles.

**Solution:**
1. Cancel the underlying async operation using `getAbortSignal()` to cause the promise to reject, allowing Svelte to clean up.
2. Avoid very long-running promises in `{#await}` blocks — add timeouts.

**Prevention:** Use `getAbortSignal()` in all async derived values and effects to enable cancellation.

---

### `<video>` muted attribute not working (issue #17327)

**Cause:** The `muted` attribute on `<video>` does not render correctly as an HTML attribute in Svelte 5.55.4 due to how boolean attributes are handled.

**Solution:**
```svelte
<!-- Workaround: set muted programmatically -->
<video
  bind:this={videoEl}
  onmount={() => videoEl.muted = true}
  autoplay
  src="/video.mp4"
></video>
```

Or use `bind:muted`:
```svelte
<script>
  let muted = $state(true);
</script>
<video bind:muted src="/video.mp4" autoplay></video>
```

**Prevention:** Always use `bind:muted` rather than the static `muted` attribute on video elements.

---

### Error: `{@const}` with `let:` slot prop causes initialization error (issue #18119)

**Cause:** Using `{@const}` to reference a `let:` slot prop in legacy slot mode causes an initialization ordering error in 5.55.4.

**Solution:**
1. Migrate to the snippets API (`{#snippet}` / `{@render}`) which does not have this issue.
2. If you must use legacy slots, compute the value inline without `{@const}`.

**Prevention:** Prefer the snippets API for all new components.

---

### Style leak with hashed filenames (issue #18092)

**Cause:** When Svelte hashes component styles using filenames and two files have the same name in different directories, their style hashes can collide, causing style leaks between components.

**Solution:**
1. Ensure component filenames are unique across the project.
2. Configure your build tool to use full path-based hashing if supported.

**Prevention:** Avoid naming multiple components identically even in different directories.

---

### `a11y-mouse-events-have-key-events` false positive with focusin/focusout (issue #8089)

**Cause:** Svelte's accessibility linter incorrectly triggers this warning when using `focusin`/`focusout` handlers, even though these are already keyboard-accessible equivalents of `mouseenter`/`mouseleave`.

**Solution:**
Suppress the warning per-component:
```javascript
// svelte.config.js
const config = {
  compilerOptions: {
    warningFilter: (w) => w.code !== 'a11y_mouse_events_have_key_events'
  }
};
```

**Prevention:** This is a known false positive — safe to suppress globally.

---

### Error: `Cannot destructure property 'x' of undefined`

**Cause:** Trying to destructure reactive state before it is initialized, often in a `$derived` that runs before async data loads.

**Solution:**
1. Provide a default value: `let data = $state({ items: [] })`.
2. Use optional chaining: `data?.items ?? []`.
3. Guard with `{#if data}` in the template.

---

### Transition not playing on initial render

**Cause:** By default, `transition:fn` does not play when an element enters on first render (only on subsequent `{#if}` changes).

**Solution:**
Use `in:fn` instead of `transition:fn`, and set `intro: true` in the `mount()` options:

```javascript
mount(App, { target, intro: true });
```

**Prevention:** Use `in:` for entrance-only transitions; set `intro: true` when mount-time animations are needed.

---

### Components not updating after store mutation

**Cause:** Directly mutating an object or array inside a `writable` store without calling `.set()` or `.update()` — stores require explicit notification.

**Solution:**
```javascript
// WRONG — no notification
items.update(v => { v.push(newItem); return v; }); // same reference

// CORRECT — return new reference or use .set
items.update(v => [...v, newItem]); // new array
// or
const current = get(items);
current.push(newItem);
items.set(current); // explicit notification
```

**Prevention:** For deeply reactive objects, prefer `$state` in `.svelte.js` modules over writable stores.

---

### SSR: `window is not defined`

**Cause:** Code that references browser globals (`window`, `document`, `navigator`) running during server-side rendering.

**Solution:**
1. Move browser-only code into `onMount` (which never runs on the server).
2. Guard with `if (typeof window !== 'undefined')`.
3. Use SvelteKit's `browser` check:
```javascript
import { browser } from '$app/environment';
if (browser) { /* browser-only code */ }
```

**Prevention:** Always guard browser-specific APIs behind `onMount` or `browser` checks.

---

### TypeScript: `Type 'X' is not assignable to type 'Y'` on props

**Cause:** Svelte's type inference for `$props()` requires explicit type annotations or the TypeScript inference cannot determine prop types.

**Solution:**
```svelte
<script lang="ts">
  interface Props {
    name: string;
    count?: number;
  }
  let { name, count = 0 }: Props = $props();
</script>
```

**Prevention:** Always type-annotate `$props()` in TypeScript components.

---

### `bind:this` returns `undefined` at first render

**Cause:** The element reference is only available after the component mounts. Accessing it synchronously in `<script>` during initialization returns `undefined`.

**Solution:**
```svelte
<script>
  let el;
  onMount(() => {
    // el is available here
    el.focus();
  });
</script>
<input bind:this={el} />
```

**Prevention:** Always access `bind:this` references inside `onMount` or lifecycle functions.

---

### `$effect` does not track all dependencies

**Cause:** Dependencies accessed via `untrack()` or accessed inside a non-tracking callback (like a `setTimeout` callback) are not tracked.

**Solution:**
1. Read all needed reactive values synchronously at the top of the `$effect` body.
2. Avoid accessing reactive state inside `setTimeout`, `Promise.then`, etc.

---

### Hydration mismatch: `hydration_attribute_changed` / `hydration_html_changed`

**Cause:** The server-rendered HTML does not match what the client-side component would render. Common causes: date/time values, random IDs, or different data on client vs. server.

**Solution:**
1. Ensure SSR and client render with identical data.
2. For truly client-only content, wrap in `{#if browser}` or use `onMount`.
3. For unavoidable mismatches, these warnings are ignorable — listed in the suppression table.

**Prevention:** Never generate random values during render. Generate them in `onMount` instead.

---

### `await_waterfall` warning

**Cause:** An `{#await}` block is nested inside another `{#await}` block, creating a sequential waterfall instead of parallel fetches.

**Solution:**
Fetch in parallel using `Promise.all`:
```svelte
<script>
  // Parallel fetch
  let data = $derived(
    Promise.all([fetchUsers(), fetchProducts()])
      .then(([users, products]) => ({ users, products }))
  );
</script>
```

**Prevention:** Use `Promise.all()` for independent async operations.

---

### Components fail to tree-shake

**Cause:** Using `export { MyComponent }` from a barrel file combined with module-level `<script module>` side effects prevents tree-shaking.

**Solution:**
1. Import directly from component files: `import MyComponent from './MyComponent.svelte'`.
2. Avoid side effects in `<script module>` blocks.

---

### `await_reactivity_loss` warning

**Cause:** Reactive state is accessed before an `await` in a derived or effect. After the `await`, Svelte can no longer track the values accessed before it.

**Solution:**
Access all reactive values **before** the first `await`:

```svelte
<script>
  let data = $derived.by(async () => {
    // Access reactive values BEFORE await
    const id = userId; // capture value
    const result = await fetch(`/api/users/${id}`);
    return result.json();
  });
</script>
```

**Prevention:** Capture all reactive reads at the beginning of async derived functions.

---

### CSS not applying to child components

**Cause:** Svelte's scoped CSS only applies to elements within the same component file. Child component elements are not styled from the parent's `<style>`.

**Solution:**
1. Use `:global()` to target specific elements: `.parent :global(.child-class) { }`.
2. Pass CSS custom properties: `<ChildComponent style="--color: red" />`.
3. Use a global stylesheet for shared styles.
4. Add the class in the child component.

---

### Performance: Too many re-renders

**Cause:** Creating new object/array references on every render pass, causing derived values and effects to see "new" values even when the content is identical.

**Solution:**
```javascript
// WRONG — new array created every render
let filtered = $derived(items.map(i => ({ ...i, label: i.name.toUpperCase() })));

// BETTER — use $derived.by with stable output
let filtered = $derived.by(() => {
  return items.map(i => ({ ...i, label: i.name.toUpperCase() }));
  // Svelte tracks specific fields, not reference equality
});
```

---

### `$effect.pending()` with `$state.eager` broken (issue #18095)

**Cause:** Known bug in Svelte 5.55.4 — `$effect.pending()` does not correctly reflect pending state when `$state.eager` values are involved.

**Solution:**
Avoid combining `$effect.pending()` and `$state.eager` until this is fixed in a future release. Use a manual loading flag instead:

```svelte
<script>
  let loading = $state(false);
  async function load() {
    loading = true;
    try { /* ... */ } finally { loading = false; }
  }
</script>
```

---

## 7. FAQ

**Q: What is the difference between Svelte and SvelteKit?**

Svelte is the UI component framework and compiler. SvelteKit is the full-stack application framework built on Svelte — it adds routing, server-side rendering, code splitting, API routes, and deployment adapters. For most new projects, start with SvelteKit. Use standalone Svelte only for component libraries or when embedding into another framework.

---

**Q: Do I need to import runes like `$state` and `$derived`?**

No. Runes are recognized by the Svelte compiler automatically. Never write `import { $state } from 'svelte'` — it will cause an error. Runes work in `.svelte` files and `.svelte.js`/`.svelte.ts` module files without any import.

---

**Q: What is the difference between `$state` and `writable`?**

`$state` is a rune that creates deeply reactive state within components and `.svelte.js` modules. It works without explicit subscription — the compiler inserts tracking automatically. `writable` from `svelte/store` creates a store object that requires explicit subscription (`.subscribe()` or the `$` prefix in templates). Both have their place: `$state` is preferred for component-local and module-level state; stores remain useful when state must be consumed outside of components (e.g., in vanilla JS).

---

**Q: Can I use Svelte 5 with existing Svelte 4 components?**

Yes. Svelte 5 is backward-compatible with Svelte 4 components. Legacy syntax (`on:click`, slots, `createEventDispatcher`) continues to work. You can gradually migrate components to runes-based syntax. Components in legacy and runes mode can be mixed in the same project.

---

**Q: How do I migrate from Svelte 4 to Svelte 5?**

Use the built-in migration tool:
```bash
npx sv migrate svelte-5
```
Or call the compiler's `migrate()` function programmatically via `svelte/compiler`. The tool converts `on:event` to `onevent`, `<slot>` to `{#snippet}`/`{@render}`, and `export let` to `$props()`.

---

**Q: When should I use `$effect` vs. `$derived`?**

Use `$derived` when you need a **computed value** — something that depends on other state and produces a result you read. Use `$effect` when you need a **side effect** — something that happens as a consequence of state changes (DOM manipulation, fetch calls, localStorage writes, analytics events). Never write back to reactive state inside `$derived`; do it in `$effect` if necessary (but prefer event handlers for user-triggered state changes).

---

**Q: How do I share state between components without prop drilling?**

Three patterns, in order of preference:
1. **`.svelte.js` module** — export reactive state: `let count = $state(0); export { count }`. All importers share the same instance.
2. **Context API** — `setContext(key, value)` in a parent, `getContext(key)` in any descendant. Good for component-tree-scoped state (like themes, modals).
3. **Svelte stores** — `writable` / `readable` from `svelte/store`. The original pattern; still works, especially for state consumed outside of components.

---

**Q: What is the `$` prefix in templates (`$store`, `$count`)?**

The `$` prefix inside `.svelte` templates auto-subscribes to a Svelte store and automatically unsubscribes when the component is destroyed. It only works in the template and script sections of `.svelte` files — not in `.js` files. Use `get(store)` for a one-time read in `.js` files.

---

**Q: How do I pass HTML content to a component?**

Use the snippets API (Svelte 5):
```svelte
<Card>
  <p>This is <strong>rich</strong> HTML content.</p>
</Card>
```
The child component receives this as the `children` snippet and renders it with `{@render children()}`. For raw HTML strings, use `{@html sanitizedString}` inside the component.

---

**Q: How do scoped styles work? Can a parent style a child component?**

Svelte compiles each component's CSS with a unique scope attribute (e.g., `svelte-xyz`). Parent styles cannot reach into child components by default — this is intentional. To style child elements from a parent:
- Use `:global(.class)` in the parent's style block
- Pass CSS custom properties from parent to child
- Let the child expose a `class` prop and apply styles externally

---

**Q: What is `tick()` and when should I use it?**

`tick()` returns a promise that resolves after Svelte has finished processing all pending state changes and updated the DOM. Use it when you need to read the updated DOM after changing state — for example, measuring element dimensions or scrolling after new content is added.

---

**Q: How do I handle errors in async components?**

Use `<svelte:boundary>` with a `failed` snippet:
```svelte
<svelte:boundary>
  <AsyncComponent />
  {#snippet failed(error, reset)}
    <p>Error: {error.message}</p>
    <button onclick={reset}>Retry</button>
  {/snippet}
</svelte:boundary>
```
For `{#await}`, use the `{:catch error}` block.

---

**Q: How do I do code splitting / lazy loading in SvelteKit?**

SvelteKit automatically code-splits at route boundaries. Each route's JavaScript is only loaded when that route is visited. For component-level lazy loading, use dynamic imports:
```javascript
const HeavyChart = await import('./HeavyChart.svelte');
```

---

**Q: Can Svelte generate static sites?**

Yes. Use `@sveltejs/adapter-static` in your `svelte.config.js`. SvelteKit will pre-render all pages to static HTML at build time. For SPA mode (client-side routing only), set `fallback: 'index.html'` in the adapter options.

---

**Q: How do I test Svelte components?**

The recommended approach is **Vitest** + **@testing-library/svelte**:
```bash
npm install --save-dev vitest @testing-library/svelte @testing-library/jest-dom jsdom
```

```javascript
import { render, screen, fireEvent } from '@testing-library/svelte';
import Counter from './Counter.svelte';

test('increments count', async () => {
  render(Counter);
  const button = screen.getByRole('button');
  await fireEvent.click(button);
  expect(screen.getByText('1')).toBeTruthy();
});
```

For end-to-end tests, use **Playwright** (included in the `sv create` scaffold).

---

**Q: What is the difference between `transition:`, `in:`, and `out:`?**

- `transition:fn` — applies the transition both on entry (when the element is added to the DOM) and exit (when it is removed).
- `in:fn` — applies only on entry.
- `out:fn` — applies only on exit.
Use `in:` and `out:` when you want different animations for enter and exit.

---

**Q: How do I animate list reordering?**

Use `animate:flip` on `{#each}` items (elements must be keyed):
```svelte
{#each items as item (item.id)}
  <div animate:flip={{ duration: 300 }}>{item.name}</div>
{/each}
```
FLIP (First, Last, Invert, Play) calculates the start and end positions and animates between them automatically.

---

**Q: How do I use Svelte with TypeScript?**

Add `lang="ts"` to your script block:
```svelte
<script lang="ts">
  interface User { id: number; name: string; }
  let user: User = $state({ id: 1, name: 'Alice' });
</script>
```
Run `npm run check` (`svelte-check`) for type checking. Generate types for SvelteKit routes by running `npm run dev` which creates `.svelte-kit/tsconfig.json` and route type definitions automatically.

---

**Q: What are `.svelte.js` files?**

Files ending in `.svelte.js` (or `.svelte.ts`) are **Svelte reactive modules** — regular JavaScript/TypeScript files where you can use runes (`$state`, `$derived`, `$effect`) outside of components. Use them to create shared reactive state, composable hooks, and reactive class instances that can be imported into multiple components.

---

**Q: How do I make a component that works as both a Svelte component and a custom element?**

Add `<svelte:options customElement="tag-name" />` to the component. During build, compile with `customElement: true`. The resulting component can be used as both an imported Svelte component and a registered HTML custom element. Note: custom elements have limitations compared to standard components (shadow DOM, attribute reflection, etc.).

---

**Q: When should I use `$state.raw` instead of `$state`?**

Use `$state.raw` for:
- Very large arrays or objects where deep proxying has a performance cost
- Data you only replace entirely (never mutate in-place)
- Third-party objects that break when wrapped in a Proxy
- Data that should not be reactive at the property level — only at the variable level

---

**Q: How do I share context between a SvelteKit layout and its pages?**

Use the SvelteKit data loading system: return data from `+layout.ts` and access it in `+page.svelte` via `$props().data`. For UI state (not data), use `setContext` in `+layout.svelte` and `getContext` in any descendant page.

---

**Q: Why does `$derived` not update when I expect it to?**

Common causes:
1. The value was accessed inside `untrack()` — removing it from the dependency graph.
2. The reactive value was accessed inside an async callback (after an `await`) — Svelte cannot track async reads.
3. You are comparing objects by reference and the same object reference is being mutated rather than replaced — use `$state.snapshot()` to debug.

---

**Q: How do I debounce a reactive value?**

```javascript
// debounced.svelte.js
export function debounced(getValue, delay = 300) {
  let debouncedValue = $state(getValue());
  let timer;

  $effect(() => {
    const current = getValue();
    clearTimeout(timer);
    timer = setTimeout(() => { debouncedValue = current; }, delay);
    return () => clearTimeout(timer);
  });

  return { get value() { return debouncedValue; } };
}
```

---

**Q: How do I upgrade from Svelte 4 stores to Svelte 5 runes?**

Stores still work in Svelte 5 — no forced migration. When you want to upgrade:
1. Move store logic to a `.svelte.js` file.
2. Replace `writable(initial)` with `let value = $state(initial)`.
3. Replace `derived(store, fn)` with `let computed = $derived(fn(value))`.
4. Replace `store.subscribe` + `store.set` with direct reads and assignments.
5. Export getter/setter objects instead of store objects.

---

**Q: What Svelte warnings can be safely suppressed?**

The following runtime warnings are documented as safe to ignore in appropriate circumstances:

| Warning Code | When Safe to Ignore |
|---|---|
| `await_waterfall` | When sequential fetching is intentional |
| `await_reactivity_loss` | When async read pattern is correct |
| `state_snapshot_uncloneable` | When you know the uncloneable item |
| `binding_property_non_reactive` | When the bound property is intentionally non-reactive |
| `hydration_attribute_changed` | When attribute differences are expected (e.g., dates) |
| `hydration_html_changed` | When HTML differences are expected |
| `ownership_invalid_binding` | During migration from legacy patterns |
| `ownership_invalid_mutation` | During migration from legacy patterns |

---

**Q: How does Svelte handle accessibility?**

Svelte compiles accessibility warnings directly from the template analyzer. It warns about:
- Missing `alt` attributes on images
- Form inputs without labels
- Interactive elements without keyboard handlers
- Invalid ARIA usage
- Color contrast (via eslint-plugin-svelte)

Configure warnings in `svelte.config.js` via `compilerOptions.warningFilter`. Note the false positive with `a11y_mouse_events_have_key_events` and `focusin`/`focusout` (issue #8089) — safe to suppress.

---

**Q: Can I use Svelte without a bundler?**

For production use, a bundler (Vite, Rollup, webpack) is required because Svelte's `.svelte` files need compilation. For quick experiments, `svelte.dev/playground` runs the compiler in the browser. The Svelte REPL at svelte.dev is the fastest way to prototype without any setup.

---

**Q: How do I profile and optimize a slow Svelte component?**

1. Use `$inspect.trace('label')` inside effects to identify what triggers re-renders.
2. Use Chrome DevTools Performance tab — look for long "scripting" frames.
3. Check for effects that run too frequently using `$effect.tracking()`.
4. For large lists, implement virtual scrolling (section 5.11).
5. Use `$state.raw` for large datasets that only need top-level reactivity.
6. Avoid creating new object/array references inside `$derived` unnecessarily.

---

**Q: Does Svelte support progressive enhancement (forms that work without JavaScript)?**

Yes, via SvelteKit. Use `<form action="/api/endpoint" method="POST">` with SvelteKit form actions. The form works without JavaScript. With JavaScript, enhance it using the `enhance` directive from `$app/forms` for a spa-like experience without a full page reload.

---

**Q: What is the difference between `onMount` cleanup and `onDestroy`?**

`onMount` accepts a function that can optionally return a cleanup function. That cleanup runs when the component is destroyed (same as `onDestroy`). The difference: `onMount` never runs during SSR, so its cleanup is also SSR-safe. `onDestroy` always runs — including during SSR — making it the right place for cleanup that needs to run in both environments.

---

**Q: How do I share Svelte components as an npm library?**

1. Set `package.json` `exports` to point to the `.svelte` source files (not compiled output).
2. Include a `svelte` field in `package.json` pointing to the main entry.
3. Add `svelte` to `peerDependencies`.
4. Consider using `@sveltejs/package` for automatic `package.json` generation.

```json
{
  "svelte": "./src/index.js",
  "exports": {
    ".": "./src/index.js",
    "./ComponentName": "./src/lib/ComponentName.svelte"
  },
  "peerDependencies": {
    "svelte": "^5.0.0"
  }
}
```

---

_Generated May 2026 from sveltejs/svelte at fcaa8ce. AI-generated documentation. The library is checked by sampling (three repositories so far); this guide was not individually reviewed. Verify against the project before relying on it. Unofficial; not affiliated with or reviewed by the project._
