Jesse A. Green / notebook

Live demo: the DocCompiler guide library as a static, read-only snapshot (October 2026). No backend, no accounts, nothing to submit.

Svelte Administrator & Developer Guide

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.

Version: 5.55.4 | Source commit: fcaa8ce | License: MIT

This guide covers the internal architecture, compiler pipeline, runtime internals, configuration, operations, and integration points for the Svelte framework. It is written for contributors, framework integrators, tooling authors, and advanced users who need to understand how Svelte works beneath the surface.


Table of Contents

  1. Architecture Overview
  2. System Requirements
  3. Configuration Reference
  4. Deployment & Scaling
  5. Operations
  6. Performance
  7. Security
  8. Developer Internals
  9. Integration Points

1. Architecture Overview

Svelte is a compile-time web framework. Unlike runtime frameworks (React, Vue), Svelte shifts the bulk of the framework work to a build step: components are compiled from .svelte source files into optimized JavaScript that directly manipulates the DOM with minimal runtime overhead.

The framework consists of two independent but cooperating systems:

System Role Location
Compiler Transforms .svelte → JS + CSS at build time packages/svelte/src/compiler/
Runtime Provides reactivity, DOM helpers, SSR, lifecycle packages/svelte/src/internal/

1.1 Compiler Pipeline (3-Phase)

The Svelte compiler transforms source text through three sequential phases. Each phase operates on the output of the previous one.

.svelte source text
       │
       ▼
┌──────────────────────────────────────┐
│  Phase 1: PARSE                      │
│  packages/svelte/src/compiler/       │
│  phases/1-parse/                     │
│  - Tokenize + parse HTML template    │
│  - Parse script blocks (acorn)       │
│  - Parse style blocks                │
│  - TypeScript support via            │
│    @sveltejs/acorn-typescript        │
│  Output: AST (Component/Module)      │
└──────────────────────────────────────┘
       │
       ▼
┌──────────────────────────────────────┐
│  Phase 2: ANALYZE                    │
│  packages/svelte/src/compiler/       │
│  phases/2-analyze/                   │
│  - Walk AST, resolve bindings        │
│  - Validate component structure      │
│  - Build scope tree                  │
│  - Check rune usage                  │
│  - CSS class/selector analysis       │
│  - Accessibility validation          │
│  Output: Annotated AST + Scope tree  │
└──────────────────────────────────────┘
       │
       ▼
┌──────────────────────────────────────┐
│  Phase 3: TRANSFORM                  │
│  packages/svelte/src/compiler/       │
│  phases/3-transform/                 │
│  - Generate JS (client or server)    │
│  - Generate CSS with scoping hashes  │
│  - Emit source maps                  │
│  Output: { js, css, warnings, ... }  │
└──────────────────────────────────────┘

NOTE: The compiler is entirely stateless — it takes source text and options, and returns output. No global state, no file system access. All file I/O happens in the calling tool (Vite plugin, Rollup plugin, etc.).

Phase 1: Parse

The parser (packages/svelte/src/compiler/phases/1-parse/) produces a structured AST from .svelte source text. The parsing strategy is HTML-first: the template is parsed as HTML, with embedded script and expression blocks extracted during traversal.

Key files:

File Purpose
index.js Entry point, orchestrates HTML + script + style parsing
read/expression.js Parses {...} template expressions using acorn
read/script.js Extracts <script> blocks and hands off to acorn/TypeScript
read/style.js Extracts <style> blocks
utils/fuzzymatch.js FuzzySet class — fuzzy string matching for error message suggestions ("did you mean X?")
utils/html.js HTML entity decoding, character utilities
utils/bracket.js Matching brace/bracket tracking in template expressions

TypeScript support is provided by @sveltejs/acorn-typescript, which extends acorn's grammar to handle TypeScript syntax inside <script lang="ts"> blocks.

Phase 2: Analyze

The analysis phase (packages/svelte/src/compiler/phases/2-analyze/) walks the AST produced by the parser and enriches it with semantic information needed for code generation.

Responsibilities:

  • Scope resolution — builds a nested scope tree resolving variable declarations, imports, and rune usages
  • Binding analysis — determines which variables are reactive ($state, $derived) and how they flow through the component
  • Rune validation — enforces correct usage of all runes (cannot call $state() at module level without $state.raw(), etc.)
  • CSS analysis — identifies which CSS selectors are "used" by the template vs. unused (for dead-code elimination)
  • Accessibility validation — checks against aria-query and axobject-query databases for ARIA attribute correctness
  • HTML tree validation — enforces valid nesting (e.g., <li> inside <ul>/<ol>, no <form> inside <form>)

The FuzzySet class from fuzzymatch.js is used during analysis to suggest correct rune/attribute names when a typo is detected.

Phase 3: Transform

The transform phase (packages/svelte/src/compiler/phases/3-transform/) takes the analyzed AST and generates the final JavaScript and CSS output. There are two transform targets:

Target Entry Point Output
Client 3-transform/client/ Browser JS — directly manipulates DOM using runtime helpers
Server 3-transform/server/ SSR JS — renders to HTML strings via server runtime

CSS transformation applies a DJB2 hash of the component filename to generate a unique class suffix for each component's styles. This is the CSS scoping mechanism (see §1.5).

Source maps are generated using magic-string (for string manipulation with position tracking) and @jridgewell/remapping (for composing source maps across multiple transformation steps).

1.2 Runtime Architecture

The runtime is split into two environments that share interface contracts but have completely different implementations.

Client Runtime (packages/svelte/src/internal/client/)

The client runtime implements Svelte's fine-grained reactivity system based on a signal graph. Key modules:

Module Purpose
runtime.js Core reactivity engine — signals, effects, reactions, dependency tracking
render.js mount(), hydrate(), unmount() — component lifecycle at the DOM level
context.js setContext() / getContext() implementation
reactivity/sources.js $state sources — mutable reactive values
reactivity/deriveds.js $derived — computed reactive values, lazy evaluation
reactivity/effects.js $effect — side effects with automatic dependency tracking
reactivity/equality.js Deep equality checks for derived invalidation
dom/blocks/if.js {#if} block rendering
dom/blocks/each.js {#each} block rendering with keying
dom/blocks/snippet.js {#snippet} rendering
dom/blocks/await.js {#await} block rendering
dom/blocks/boundary.js Error/Svelte boundary blocks
dom/elements/ Element-level helpers (events, bindings, transitions, animations)
errors.js Generated runtime error definitions
warnings.js Generated runtime warning definitions

Signal graph model: $state creates sources (writable nodes). $derived creates derived nodes (read-only, computed lazily). $effect creates effect nodes (run on change). When a source changes, all downstream deriveds are marked dirty and all downstream effects are scheduled for re-run. Dependency edges are tracked automatically during execution — any source read inside a derived or effect registers itself as a dependency.

Batching: Multiple state mutations can be batched to defer effect re-runs until the end of the batch, preventing cascading intermediate renders.

Server Runtime (packages/svelte/src/internal/server/)

The server runtime implements SSR. Key characteristics:

  • Renders to HTML strings (or ReadableStream for streaming SSR)
  • Most lifecycle hooks (onMount, beforeUpdate, afterUpdate) are noops on the server
  • onDestroy is the one lifecycle that runs on server — used for cleanup in SSR contexts
  • No reactivity graph — state is read-only during server rendering
  • Hydration markers are injected into the output HTML for client takeover

1.3 Hydration System

Svelte uses comment-based hydration markers to identify dynamic DOM regions during client-side takeover of server-rendered HTML.

Marker constant Value Meaning
HYDRATION_START [ Begin hydration block
HYDRATION_START_ELSE [! Begin else branch
HYDRATION_START_FAILED [? Begin failed state (boundary)
HYDRATION_END ] End hydration block

The client runtime (render.js) walks the DOM looking for these markers when hydrate() is called instead of mount(). On hydration:

  1. Server-rendered HTML is preserved as-is where possible
  2. Event listeners are attached
  3. Reactive state is initialized
  4. Dynamic regions bounded by markers are reconciled with the initial state

Hydration mismatches are caught and reported via the following ignorable (non-fatal) warnings:

hydration_attribute_changed   — attribute value differs between SSR and client
hydration_html_changed        — raw HTML content differs between SSR and client

NOTE: These warnings indicate that SSR output diverges from what the client would render independently. They are safe to suppress in development but should be investigated in production as they represent inconsistent initial state.

1.4 CSS Scoping

Svelte generates scoped CSS by appending a unique class to every selector in a component's <style> block, and adding that class to every element in the component's template.

The scope hash is generated by the hash() function in packages/svelte/src/utils.js:

// DJB2 hash → base-36 string
// Example: "Button.svelte" → "svelte-1x2y3z"
hash(str: string): string

The hash input is derived from the component's filename (configurable via cssHash compiler option). This means:

  • Same file = same hash across builds (deterministic)
  • Different files = different hashes (no collisions)
  • Hash is a short base-36 string prepended with svelte-

CSS selectors are transformed at compile time:

/* Input (in Button.svelte) */
button { color: red; }

/* Output */
button.svelte-abc123 { color: red; }

Template elements receive the class at compile time, not runtime — adding zero runtime overhead.

1.5 Event Delegation

Rather than attaching individual event listeners to each DOM element, Svelte delegates a fixed set of events to the document root. This reduces memory usage and listener overhead for large component trees.

Delegated events (attached once at document root):

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

Passive events (registered with { passive: true }):

touchstart, touchmove

The can_delegate_event(name) utility in packages/svelte/src/utils.js checks whether an event name is in the delegated set. Events not in this list receive per-element listeners.

1.6 HTML Tree Validation

The compiler validates HTML nesting rules at compile time (html-tree-validation.js in the analyze phase).

Autoclosing children (elements that implicitly close when another is opened):

li, dt, dd, p, rt, rp, optgroup, option,
thead, tbody, tfoot, tr, td, th

Disallowed nesting (hard errors):

  • <form> inside <form>
  • <a> inside <a>
  • <button> inside <button>
  • Heading elements nested inside each other (<h1> inside <h2>, etc.)

NOTE: Custom elements (tag names containing a -, e.g., <my-button>) bypass all HTML tree validation. This is intentional — custom element nesting rules are not standardized and vary by implementation.

1.7 Namespace Support

Svelte handles three XML namespaces:

Constant Value Usage
NAMESPACE_HTML http://www.w3.org/1999/xhtml Default namespace
NAMESPACE_SVG http://www.w3.org/2000/svg <svg> subtrees
NAMESPACE_MATHML http://www.w3.org/1998/Math/MathML <math> subtrees

The compiler detects when it's inside an SVG or MathML context (using is_svg(name) / is_mathml(name) from utils.js) and adjusts element creation calls accordingly. SVG/MathML elements must be created with document.createElementNS() rather than document.createElement().

Template flags control this behavior at runtime:

Flag Value Meaning
TEMPLATE_FRAGMENT 1 Template is a document fragment
TEMPLATE_USE_IMPORT_NODE 2 Use document.importNode()
TEMPLATE_USE_SVG 4 Template is SVG namespace
TEMPLATE_USE_MATHML 8 Template is MathML namespace

1.8 Version Disclosure

When Svelte mounts in a browser, it adds its version to a global set:

// packages/svelte/src/internal/disclose-version.js
window.__svelte = window.__svelte || { v: new Set() };
window.__svelte.v.add(VERSION);

This allows browser devtools and diagnostics to detect which version(s) of Svelte are running on a page (multiple versions can coexist).


2. System Requirements

2.1 Runtime Requirements (for framework users)

Requirement Minimum Notes
Node.js 18.x Required for build tooling; not present in browser bundles
pnpm 9.0.0+ Required for monorepo development; npm/yarn work for consuming the package
Browsers Modern ES2020+ output by default; exact targets depend on Vite/Rollup config

2.2 Browser Support

Svelte's compiled output uses:

  • document.createElement / createTextNode / createComment
  • element.cloneNode(true) (for template cloning)
  • addEventListener / removeEventListener
  • ES2020 features (optional chaining, nullish coalescing, Promise.allSettled)
  • Proxy (for fine-grained $state object reactivity)
  • WeakMap, WeakRef (for memory-safe reactivity bookkeeping)

Minimum practical browser support: Chrome 80+, Firefox 78+, Safari 14+, Edge 80+.

For legacy browser support, downstream build tools must transpile and polyfill the compiled output.

2.3 Server-Side Rendering Requirements

SSR is pure JavaScript with no DOM dependency. The server runtime requires:

  • A Node.js (or compatible) runtime
  • No browser globals (window, document) — these are explicitly not referenced in the server runtime

2.4 Development Machine Requirements

For contributing to the Svelte monorepo:

node --version   # must be >= 18
pnpm --version   # must be >= 9.0.0

Optional but recommended:

  • Playwright browsers for e2e tests: pnpm exec playwright install
  • TypeScript language server for IDE support

3. Configuration Reference

3.1 compile(source, options) — Compiler API

The primary compiler entry point. Compiles a .svelte component to JavaScript and CSS.

import { compile } from 'svelte/compiler';

const result = compile(source: string, options: CompileOptions): CompileResult;

CompileOptions (all fields optional unless noted):

Option Type Default Description
filename string undefined Source filename. Used for CSS scoping hash and source map sources. Should be set in all real usage.
name string 'Component' Component class name in the generated JS.
generate 'client' | 'server' | false 'client' Target environment. false returns AST only (no code generation).
dev boolean false Enable development mode. Adds runtime checks, detailed error messages, $inspect support.
accessors boolean false (Legacy) Generate getters/setters for props on component instances. Svelte 5 uses $bindable instead.
immutable boolean false (Legacy) Assume props are immutable objects.
hydratable boolean false Emit hydration markers for SSR takeover. Must match server-side generate: 'server' option.
legacy boolean | object false Enable Svelte 4 compatibility mode. Accepts { componentApi: 4 } to use the Svelte 4 component API.
customElement boolean false Compile as a custom element (Web Component).
namespace 'html' | 'svg' | 'mathml' 'html' Root namespace of the component. Set to 'svg' for SVG-only components.
cssHash (args: { hash, css, name, filename }) => string internal Override CSS scope class generation. Default uses DJB2 hash of filename.
preserveComments boolean false Include HTML comments in compiled output.
preserveWhitespace boolean false Preserve insignificant whitespace between elements.
runes boolean | undefined undefined Force runes mode (true) or legacy mode (false). undefined = auto-detect from source.
modernAst boolean false Return the modern (Svelte 5) AST format from parse().

CompileResult:

{
  js: {
    code: string;
    map: SourceMap | null;
  };
  css: {
    code: string;
    map: SourceMap | null;
  } | null;
  warnings: Warning[];
  metadata: {
    runes: boolean;  // whether the component uses runes
  };
}

3.2 compileModule(source, options) — Module Compiler

Compiles .svelte.js / .svelte.ts module files (files that use Svelte runes but are not components — no template).

import { compileModule } from 'svelte/compiler';

const result = compileModule(source: string, options: ModuleCompileOptions): CompileResult;

ModuleCompileOptions:

Option Type Default Description
filename string undefined Source filename
generate 'client' | 'server' 'client' Target environment
dev boolean false Development mode

3.3 parse(source, options) — Parser API

Parses a .svelte source string to AST without code generation.

import { parse } from 'svelte/compiler';

const ast = parse(source: string, options?: ParseOptions): AST.Root;

ParseOptions:

Option Type Default Description
filename string undefined Source filename for error messages
modern boolean false Return modern AST format (Svelte 5 node types)

The returned AST.Root contains:

  • html — fragment node with the component's template tree
  • instance — <script> block AST (if present)
  • module — <script context="module"> / <script module> AST (if present)
  • css — <style> block AST (if present)

3.4 preprocess(source, preprocessors, options) — Preprocessor API

Runs one or more preprocessors over a .svelte source string before compilation.

import { preprocess } from 'svelte/compiler';

const result = await preprocess(
  source: string,
  preprocessors: PreprocessorGroup | PreprocessorGroup[],
  options?: { filename?: string }
): Promise<Processed>;

PreprocessorGroup: An object with optional markup, script, and style async functions. Each receives the relevant portion of the source and returns { code, map?, dependencies? }.

Processed:

{
  code: string;         // transformed source
  map: SourceMap | null;
  dependencies: string[];  // files to watch for changes
  toString(): string;
}

Common preprocessors consumed via this API:

  • svelte-preprocess — TypeScript, SCSS, PostCSS, Pug support
  • @sveltejs/vite-plugin-svelte — handles preprocessing transparently in Vite setups

3.5 migrate(source, options) — Migration API

Migrates Svelte 4 component syntax to Svelte 5 runes syntax.

import { migrate } from 'svelte/compiler';

const result = migrate(source: string, options?: MigrateOptions): {
  code: string;
  // migration may not be complete for all patterns
};

IMPORTANT: The migration tool handles the most common Svelte 4 patterns but may not cover all edge cases. Always review migrated output manually, especially for complex reactive statements ($:) and slot usage.

3.6 svelte.config.js — Project Configuration

The canonical project configuration file, consumed by SvelteKit and @sveltejs/vite-plugin-svelte.

// 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(),

  // Svelte compiler options (passed to compile())
  compilerOptions: {
    // Options from §3.1, minus filename/name (set per-file by the plugin)
    runes: true,       // force runes mode project-wide
    dev: false,        // overridden by plugin based on Vite mode
  },

  // Vite plugin options (§3.7)
  vitePlugin: {
    inspector: true,
  },

  // SvelteKit-specific options (if using SvelteKit)
  kit: {
    adapter: adapter(),
  },
};

export default config;

3.7 Vite Plugin Options (@sveltejs/vite-plugin-svelte)

When using Svelte with Vite directly (without SvelteKit):

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

export default defineConfig({
  plugins: [
    svelte({
      // All svelte.config.js options are accepted here too.
      // These take precedence if svelte.config.js also exists.

      include: /\.svelte$/,        // which files to process
      exclude: [],                  // which files to skip
      extensions: ['.svelte'],      // treated as Svelte files

      emitCss: true,               // extract CSS into separate chunks
      hot: true,                   // HMR (auto-disabled in SSR mode)

      preprocess: [],              // PreprocessorGroup[]
      compilerOptions: {},         // CompileOptions (§3.1)

      onwarn(warning, defaultHandler) {
        // filter or transform compiler warnings
        if (warning.code === 'a11y-click-events-have-key-events') return;
        defaultHandler(warning);
      },
    }),
  ],
});

3.8 Complete Runes Reference

Runes are special compiler directives — not real function calls, but syntax recognized by the Svelte compiler.

State creation runes:

Rune Description
$state(initial) Reactive state. Objects/arrays are deeply reactive via Proxy.
$state.raw(initial) Reactive state without deep proxy wrapping. Assignment triggers update; mutation does not.
$state.snapshot(value) Takes a non-reactive deep copy of a $state value.
$state.eager(initial) State that immediately runs effects (vs. default batched/deferred).
$derived(expression) Computed value, re-evaluated when dependencies change.
$derived.by(() => expression) Like $derived but accepts a function (for multi-statement deriveds).

Props runes:

Rune Description
$props() Declare component props. Destructure to access individual props.
$props.id() Get the component's stable identity string (for aria-labelledby, etc.).
$bindable(default?) Mark a prop as bindable (allows bind:prop from parent).

Effect runes:

Rune Description
$effect(() => { ... }) Runs after DOM update when dependencies change. Cleanup via returned function.
$effect.pre(() => { ... }) Like $effect but runs before DOM update.
$effect.tracking() Returns true if called inside a tracking context (effect/derived).
$effect.root(() => { ... }) Creates an isolated effect root not tied to component lifecycle.
$effect.pending() Returns true if any $state.eager updates are pending.

Utility runes:

Rune Description
$inspect(value) Dev-mode only. Logs value and its dependencies to console.
$inspect(value).with(fn) Custom inspector callback instead of console.log.
$inspect.trace(label?) Traces which reactive sources triggered the current effect.
$host() Inside custom elements only — returns the host DOM element.

4. Deployment & Scaling

4.1 SSR Deployment

Svelte components compiled with generate: 'server' output a module with a render() function:

// Compiled server output (simplified)
export function render(props, options) {
  const html = /* ... server-rendered HTML string ... */;
  const head = /* ... <head> content ... */;
  return { html, head };
}

SvelteKit handles this automatically via its adapter system. For manual SSR setups:

import Component from './Component.svelte';

// Server handler (Express/Node example)
app.get('/', (req, res) => {
  const { html, head } = Component.render({ prop: 'value' });
  res.send(`
    <!DOCTYPE html>
    <html>
      <head>${head}</head>
      <body>${html}</body>
    </html>
  `);
});
// Client hydration
import { hydrate } from 'svelte';
import Component from './Component.svelte';

hydrate(Component, {
  target: document.body,
  props: { prop: 'value' },
});

IMPORTANT: The hydratable: true compiler option must be set for both the server and client builds. Without it, hydration markers are not emitted and hydrate() falls back to full client-side rendering.

4.2 Static Site Generation

For fully static output (no server runtime), prerender pages at build time. With SvelteKit:

// src/routes/+page.js
export const prerender = true;

For standalone Svelte without SvelteKit, the pattern is:

  1. Compile components with generate: 'server'
  2. Run server rendering in a Node.js build script
  3. Write HTML files to disk
  4. Serve as static files

4.3 Custom Element Compilation

Svelte components can be compiled to native Web Components (custom elements):

// compile option
compile(source, {
  customElement: true,
  name: 'my-button',  // must contain a hyphen
});

Or declare per-component in the <svelte:options> tag:

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

Custom element behavior:

  • Props become observed attributes (with type coercion)
  • <style> is encapsulated in a Shadow DOM
  • Lifecycle maps to connectedCallback / disconnectedCallback
  • $host() rune provides access to the host element
  • HTML tree validation is disabled for custom elements (see §1.6)

Custom element options (via <svelte:options customElement={{ ... }}):

<svelte:options customElement={{
  tag: 'my-button',
  shadow: 'open',        // 'open' | 'none'
  props: {
    label: {
      reflect: true,     // sync prop changes back to attribute
      type: 'String',    // 'Boolean' | 'Number' | 'String' | 'Array' | 'Object'
      attribute: 'label' // attribute name (defaults to lowercased prop name)
    }
  },
  extend: (customElementClass) => class extends customElementClass {
    // custom lifecycle hooks
  }
}} />

4.4 Adapter Pattern (SvelteKit)

SvelteKit uses adapters to deploy to different hosting targets. An adapter is a package that:

  1. Receives the SvelteKit build output
  2. Transforms it for the target platform
  3. Writes deployment files

Adapter interface (for custom adapter authors):

/** @type {import('@sveltejs/kit').Adapter} */
export default function myAdapter(options = {}) {
  return {
    name: 'adapter-my-target',
    async adapt(builder) {
      // builder.generateManifest(), builder.copy(), builder.prerender(), etc.
    },
    supports: {
      read: () => true,  // whether adapter supports FS reads in load functions
    },
  };
}

4.5 Output Targets

Svelte's compiled output can target multiple module formats via the downstream bundler configuration. The Svelte compiler itself outputs ES module syntax; transformation to CJS/UMD/IIFE is handled by Rollup/Vite/webpack.

For libraries (not applications), the recommended distribution format is ESM with exports map in package.json, following Svelte's own package structure.

4.6 Multi-Version Coexistence

Multiple versions of Svelte can technically coexist on a single page (each adds its version to window.__svelte.v). However, this is not recommended for production:

  • Each version carries its own runtime overhead
  • Component trees from different versions cannot share context or stores
  • Event delegation tables are per-version and may conflict

5. Operations

5.1 Monorepo Structure

svelte-monorepo/                    ← root package (name: "svelte-monorepo")
  packages/
    svelte/                         ← main package (published as "svelte")
      src/
        compiler/                   ← compiler source
          phases/
            1-parse/
            2-analyze/
            3-transform/
              client/
              server/
          utils/
        internal/                   ← runtime source
          client/
            dom/
              blocks/
              elements/
            reactivity/
          server/
          flags/                    ← experimental flag modules
        index.js                    ← public exports (non-compiler)
        index.d.ts                  ← TypeScript type definitions
      package.json
      rollup.config.js              ← build config for this package
  sites/
    svelte.dev/                     ← documentation site
    learn.svelte.dev/               ← interactive tutorial
  playgrounds/
    sandbox/                        ← Vite-based REPL/playground
  package.json                      ← root workspace config
  pnpm-workspace.yaml               ← pnpm workspace definition

5.2 Build System

The package is built with Rollup (not Vite). Vite is used only for the playground/sandbox.

Root-level scripts (package.json at monorepo root):

Script Command Purpose
build pnpm -r build Build all packages recursively
check svelte-check Type-check all .svelte files
lint eslint . Run ESLint
format prettier --write . Format all files
test vitest Run unit tests
bench vitest bench Run benchmarks
bench:compare custom Compare benchmark results
bench:debug custom Benchmark with debugging
changeset:version changeset version Bump version from changesets
changeset:publish changeset publish Publish to npm

Package-level scripts (packages/svelte/package.json):

Script Purpose
build rollup -c then pnpm generate
dev rollup -c -w (watch mode)
check TypeScript type checking
generate Run all code generation scripts
generate:version Write src/version.js from package.json
generate:types dts-buddy — generate .d.ts from source
prepublishOnly Full build + type generation before publish
knip knip — detect dead exports/imports

Build process in detail:

# Full build of packages/svelte
cd packages/svelte
pnpm build
# 1. rollup -c           → bundles src/ → dist/
# 2. pnpm generate       → code-gen (version.js, error messages, types)
# 3. dts-buddy           → generates TypeScript declarations

The Rollup configuration (packages/svelte/rollup.config.js) produces multiple output bundles corresponding to the package exports map (see §3.8 below).

5.3 Code Generation

Several source files in packages/svelte/src/ are generated and should not be hand-edited:

Generated file Generator Content
src/version.js generate:version export const VERSION = '5.55.4'
src/internal/client/errors.js generate:errors Runtime error message functions
src/internal/client/warnings.js generate:warnings Runtime warning functions
src/compiler/errors.js generate:errors Compiler error message functions
src/compiler/warnings.js generate:warnings Compiler warning functions

Error and warning functions follow a pattern:

// Generated — do not edit
export function invalid_rune_usage(node, rune) {
  // throws CompileDiagnostic with URL link
  e(node, "invalid_rune_usage", `The \`${rune}\` rune can only be used inside a .svelte or .svelte.js file\nhttps://svelte.dev/e/invalid_rune_usage`);
}

5.4 Testing Strategy

Tests live in packages/svelte/tests/ and are organized by category:

Category Directory Runner Purpose
Runtime tests/runtime/ Vitest Component behavior in jsdom
CSS tests/css/ Vitest CSS scoping, selector transforms
Parser tests/parser/ Vitest AST output for given inputs
Validator tests/validator/ Vitest Compiler error/warning triggering
Hydration tests/hydration/ Vitest SSR → client hydration correctness
Server tests/server/ Vitest SSR output correctness
Motion tests/motion/ Vitest svelte/motion tweens/springs
Transition tests/transition/ Vitest svelte/transition functions
e2e tests/e2e/ Playwright Full browser interaction tests

Run tests:

# All tests
pnpm test

# Specific category (Vitest filter)
pnpm test -- --reporter=verbose runtime

# e2e (requires Playwright browsers)
pnpm exec playwright install
pnpm test:e2e

Each test category follows a fixture pattern: a directory of input files (input.svelte, input.js) and expected output files (output.js, expected.css). The test runner compiles the input and diffs against the expected output.

To update expected outputs after intentional changes:

pnpm test -- --update-snapshots

5.5 Changesets Workflow

Version management uses changesets (from @changesets/cli).

Adding a changeset (required for any user-visible change):

pnpm changeset
# Interactive prompt:
# 1. Select packages changed (svelte)
# 2. Select semver bump (major/minor/patch)
# 3. Write summary of change
# Creates .changeset/random-name.md

Release process:

# 1. Accumulate changesets from PRs
# 2. Version bump (updates CHANGELOG.md, package.json)
pnpm changeset:version

# 3. Review CHANGELOG, commit
git commit -am "chore: version packages"

# 4. Publish to npm
pnpm changeset:publish
# Internally runs: pnpm prepublishOnly && npm publish

5.6 Linting Configuration

ESLint is configured with a custom rule:

no_compiler_imports — Prevents any file under packages/svelte/src/ (excluding compiler/) from importing anything from packages/svelte/src/compiler/. This enforces the hard separation between compiler and runtime — the runtime must not depend on the compiler.

Location: eslint-plugin/ or inline in eslint.config.js.

Prettier handles formatting. Configuration is in .prettierrc at the repository root:

  • Tab width: configurable (check .prettierrc)
  • Single quotes vs double: per config
  • No semicolons: per config

5.7 Benchmarking

Svelte maintains a benchmark suite for tracking reactivity and rendering performance:

# Run benchmarks
pnpm bench

# Compare two commits
pnpm bench:compare

# Debug benchmark with verbose output
pnpm bench:debug

Benchmarks use Vitest's bench API and measure:

  • Reactivity update propagation throughput
  • DOM rendering time for large {#each} blocks
  • Keyed vs. non-keyed reconciliation

5.8 Continuous Integration

The repository uses GitHub Actions. Key CI checks:

Check Trigger Purpose
Type check PR, push tsc over all packages
Lint PR, push ESLint + Prettier check
Unit tests PR, push Full Vitest suite
e2e tests PR, push Playwright browser tests
Benchmark Scheduled Regression detection

6. Performance

6.1 Event Delegation Model

Delegating events to the document root instead of per-element provides significant memory savings for large component trees. A list of 10,000 items with click handlers uses 1 document listener instead of 10,000.

The delegation layer dispatches to the correct handler via a lookup table keyed by the target element, walking up the DOM tree until a handler is found (event bubbling is preserved semantically).

Implications for integrators:

  • event.stopPropagation() works as expected within Svelte trees
  • Events from non-Svelte DOM (e.g., vanilla JS children) are not captured by the delegation table
  • The delegation table is set up once when any component using a given event type is first mounted

6.2 CSS Scoping Strategy

Zero-runtime-cost CSS scoping: the scope class (e.g., .svelte-abc123) is added to DOM elements by the compiled code at element creation time, not by runtime class manipulation. There is no className mutation after initial render.

CSS specificity is unchanged — scope class adds one class-level specificity point, same for all selectors in the file.

CSS dead code elimination: The compiler tracks which selectors are referenced in the template. Unused selectors are removed from the output CSS entirely (with a dev-mode warning). This is done in the analyze phase.

6.3 Reactivity Optimization

Fine-grained updates: Only the exact DOM nodes that depend on a changed signal are updated. There is no virtual DOM diffing — the signal graph encodes exactly which DOM operation corresponds to which reactive value.

Lazy derivation: $derived values are not computed until read. If a derived is never read in a given render cycle, it is never computed.

Batching: Multiple $state mutations in a single synchronous block are batched — effects run once after all mutations, not once per mutation.

Keyed {#each} blocks: Using a key expression ({#each items as item (item.id)}) enables efficient list reconciliation. Unkeyed each blocks rebuild their DOM on reorder. A known performance regression was identified in issue #18120: keyed {#each} is currently slower than non-keyed in certain scenarios — track for fix.

6.4 Hydration Performance

Svelte's hydration avoids full re-rendering:

  • Static HTML from SSR is preserved as-is
  • Only dynamic regions (bounded by hydration markers) are visited
  • Event listeners are attached in a single pass over the hydration markers
  • $state is initialized from the rendered values where possible

6.5 Tree-Shaking

Each export in the Svelte package is side-effect-free (marked in package.json with "sideEffects": false or equivalent). Bundlers can eliminate unused exports:

  • A project that uses no transitions imports nothing from svelte/transition
  • SSR builds tree-shake away client-only DOM manipulation code
  • The esm-env package provides BROWSER/SERVER constants that enable static dead-code elimination by bundlers

6.6 Template Cloning

For elements rendered in {#each} loops, Svelte uses document.importNode() (when TEMPLATE_USE_IMPORT_NODE flag is set) to clone a pre-built template fragment instead of creating elements one by one with createElement. This is faster for complex subtrees.


7. Security

7.1 HTML Escaping

User-provided strings rendered inside {expression} template interpolations are HTML-escaped by default. The escape function is escape_html() in the runtime.

Characters escaped: &, <, >, ", '

<!-- Safe: user input is escaped -->
<p>{userInput}</p>

<!-- UNSAFE: raw HTML injection — only use with trusted content -->
<p>{@html trustedHtmlString}</p>

WARNING: {@html ...} bypasses HTML escaping entirely and injects raw HTML into the DOM. Never use {@html} with unsanitized user input. If user content must be rendered as HTML, sanitize it with a library like DOMPurify before passing to {@html}.

7.2 Trusted Types Support

Svelte includes support for the Trusted Types API, a browser security feature that prevents DOM XSS by enforcing that only "trusted" values can be assigned to dangerous sinks like innerHTML.

The @types/trusted-types dev dependency provides TypeScript types for the Trusted Types API. Svelte's runtime respects Trusted Types policies when setting innerHTML for {@html} blocks — it will call the active policy's createHTML() method if a policy is enforced.

To use Svelte with a Trusted Types CSP:

Content-Security-Policy: require-trusted-types-for 'script'; trusted-types svelte-policy;

7.3 CSS Injection Prevention

The CSS scoping mechanism (§1.4) means component styles cannot leak out and affect other components' elements. This prevents:

  • Third-party component styles from overriding application styles unexpectedly
  • User-provided class names from triggering unintended style application

However, CSS scoping is not a security boundary — it's a specificity mechanism. Dynamic style bindings (style:property={value}) that include user input should be validated:

<!-- Potentially dangerous if `userColor` is not validated -->
<div style:color={userColor}>...</div>

CSS custom properties set via --property={value} bindings are safe — they cannot inject new CSS rules, only set values.

7.4 XSS Prevention in Templates

Svelte's compile-time template processing provides strong XSS protection:

Template syntax Safety
{expression} Safe — HTML-escaped
{@html expression} Unsafe — raw injection, use with caution
on:click={handler} Safe — compiles to addEventListener, not inline script
href={value} Safe for https:// URLs; javascript: is blocked in dev mode
style:prop={value} Safe — CSS property values, not rule injection

CAUTION: Svelte does not sanitize href attribute values in production builds beyond emitting a dev-mode warning for javascript: URLs. Validate user-provided URLs server-side before binding to href.

7.5 svelte/internal Import Guard

packages/svelte/src/internal/index.js deliberately throws a runtime error when imported:

// svelte/internal — DO NOT IMPORT
// This module existed in Svelte 4 but is private in Svelte 5.
// The internal API changed incompatibly between versions.
// Import from svelte, svelte/store, svelte/motion, etc. instead.
throw new Error('...');

This is enforced at runtime (not just as a TypeScript type error) to prevent libraries and user code from depending on private internals that may change without notice.

The ESLint rule no_compiler_imports enforces the equivalent boundary at development time within the monorepo itself.

7.6 Content Security Policy

Svelte's compiled output does not use eval() or Function() constructor by default — all reactivity and DOM manipulation is compiled to static code. This makes Svelte compatible with strict CSP script-src without unsafe-eval.

Exception: Some legacy compatibility features in Svelte 4 mode may require unsafe-eval. Svelte 5 runes mode is unsafe-eval-free.


8. Developer Internals

8.1 Complete Repository Structure

packages/svelte/
  src/
    compiler/
      phases/
        1-parse/
          index.js                ← parser entry point
          read/
            expression.js         ← template expression parser
            script.js             ← <script> block parser
            style.js              ← <style> block parser
          utils/
            fuzzymatch.js         ← FuzzySet for error suggestions
            html.js               ← HTML entity utils
            bracket.js            ← bracket matching
        2-analyze/
          index.js                ← analysis entry point
          css/                    ← CSS analysis
          a11y.js                 ← accessibility validation
          html-tree-validation.js ← nesting validation
        3-transform/
          client/                 ← client-side code generation
          server/                 ← server-side code generation
          css/                    ← CSS transform + scoping
      utils/
        compile_diagnostic.js     ← CompileDiagnostic class
        mapped_code.js            ← MappedCode class
      errors.js                   ← GENERATED compiler errors
      warnings.js                 ← GENERATED compiler warnings
      index.js                    ← svelte/compiler exports
    internal/
      client/
        dom/
          blocks/
            if.js                 ← {#if} rendering
            each.js               ← {#each} rendering
            snippet.js            ← {#snippet} rendering
            await.js              ← {#await} rendering
            boundary.js           ← error boundary rendering
            key.js                ← {#key} block
            html.js               ← {@html} rendering
            svelte-element.js     ← <svelte:element>
            svelte-component.js   ← <svelte:component>
          elements/
            events.js             ← event handling + delegation
            bindings/             ← bind: directives
            transitions.js        ← transition: directives
            animations.js         ← animate: directives
            attachments.js        ← {@attach} directive
        reactivity/
          sources.js              ← $state implementation
          deriveds.js             ← $derived implementation
          effects.js              ← $effect implementation
          equality.js             ← deep equality
        runtime.js                ← signal graph core
        render.js                 ← mount/hydrate/unmount
        context.js                ← setContext/getContext
        errors.js                 ← GENERATED runtime errors
        warnings.js               ← GENERATED runtime warnings
      server/
        index.js                  ← SSR rendering
        context.js                ← server context
      flags/
        async.js                  ← experimental async flag
        legacy.js                 ← legacy mode flag
        tracing.js                ← effect tracing flag
      disclose-version.js         ← window.__svelte.v registration
      index.js                    ← throws error (private guard)
    constants.js                  ← all bitflag constants
    utils.js                      ← shared utility functions
    index.js                      ← public API (non-compiler)
    index.d.ts                    ← TypeScript declarations
    store/                        ← svelte/store package
    motion/                       ← svelte/motion package
    transition/                   ← svelte/transition package
    animate/                      ← svelte/animate package
    easing/                       ← svelte/easing package
    events/                       ← svelte/events package
    attachments/                  ← svelte/attachments package
    reactivity/                   ← svelte/reactivity package
    legacy/                       ← svelte/legacy package
  dist/                           ← built output (gitignored)
  package.json
  rollup.config.js

8.2 Package Exports Map

The packages/svelte/package.json exports map defines all public entry points:

Export Condition Purpose
. browser → client, default → server Main runtime (auto-selects client/server)
./compiler — Compiler API (compile, parse, etc.)
./store — Svelte store primitives (writable, readable, derived)
./motion — tweened, spring motion stores
./transition — Built-in transitions (fade, fly, slide, etc.)
./animate — Built-in animations (flip)
./easing — Easing functions
./events — on() utility for event handling
./attachments — {@attach} directive utilities
./reactivity — Standalone reactive primitives
./reactivity/window — Window-level reactive values
./server — Explicit server runtime import
./legacy — Svelte 4 compatibility API
./internal — Throws error — private, do not use
./internal/client — Internal client runtime (semi-public for tooling)
./internal/server — Internal server runtime (semi-public for tooling)
./internal/flags/* — Feature flag modules

8.3 Bitflag Constants

Svelte uses integer bitflags extensively for compact state representation. Understanding these is essential for reading the compiler and runtime source.

{#each} block flags (EACH_*):

Constant Value Meaning
EACH_ITEM_REACTIVE 1 Items are reactive signals
EACH_INDEX_REACTIVE 2 Index is reactive
EACH_IS_CONTROLLED 4 Each block is "controlled" (no wrapping element)
EACH_IS_ANIMATED 8 Each uses animate: directive
EACH_ITEM_IMMUTABLE 16 Items treated as immutable

Props flags (PROPS_*):

Constant Value Meaning
PROPS_IS_IMMUTABLE 1 Props object is immutable
PROPS_IS_RUNES 2 Parent is in runes mode
PROPS_IS_UPDATED 4 Props have been updated at least once
PROPS_IS_BINDABLE 8 Prop is bindable ($bindable)
PROPS_IS_LAZY_INITIAL 16 Initial value is lazily evaluated

Transition flags (TRANSITION_*):

Constant Value Meaning
TRANSITION_IN 1 Transition plays on enter
TRANSITION_OUT 2 Transition plays on exit
TRANSITION_GLOBAL 4 Global transition (not scoped to block)

Usage pattern in source:

// Setting flags
const flags = EACH_ITEM_REACTIVE | EACH_INDEX_REACTIVE;  // = 3

// Checking flags
if (flags & EACH_IS_ANIMATED) { ... }

// Clearing a flag
flags &= ~EACH_ITEM_REACTIVE;

8.4 Signal Graph Implementation

The core reactivity engine in runtime.js implements a push-pull signal graph:

Node types:

  • Source (writable signal) — created by $state. Has a value and a set of subscribers.
  • Derived (computed signal) — created by $derived. Has a computation function, cached value, and dirty flag.
  • Effect — created by $effect. Has a side-effect function and cleanup return value.

Dependency tracking:

  • A global "current consumer" pointer is set when running a derived or effect
  • Any source read during execution registers the current consumer as a subscriber
  • Reads outside a tracking context (no current consumer) do not register subscriptions

Update cycle:

  1. Source value changes → mark all subscribers dirty
  2. Schedule microtask to flush effects
  3. In flush: run dirty effects in topological order
  4. Effects re-run their function, re-registering dependencies (stale dependencies are pruned)
  5. Cleanup functions from previous run are called before re-run

Effect scheduling:

  • Effects are scheduled via Promise.resolve().then() (microtask queue)
  • $effect.pre runs before DOM updates (synchronous flush)
  • $effect runs after DOM updates (async flush)

8.5 Compiler AST Node Types

The Svelte AST (returned by parse()) uses these primary node types:

Fragment nodes:

  • Root — top-level component AST
  • Fragment — ordered list of child nodes

Template nodes:

  • Element — HTML elements (<div>, <span>, etc.)
  • Component — Svelte components (<MyComponent>)
  • SvelteComponent — <svelte:component>
  • SvelteElement — <svelte:element>
  • SvelteSelf — <svelte:self> (recursive component)
  • SvelteFragment — <svelte:fragment>
  • SvelteHead — <svelte:head>
  • SvelteBody — <svelte:body>
  • SvelteWindow — <svelte:window>
  • SvelteDocument — <svelte:document>
  • SvelteOptions — <svelte:options>
  • Text — static text node
  • ExpressionTag — {expression} interpolation
  • HtmlTag — {@html expression} raw HTML
  • ConstTag — {@const x = ...} local constant
  • DebugTag — {@debug variable} debug helper
  • RenderTag — {@render snippet()} snippet invocation
  • AttachTag — {@attach action} attachment

Block nodes:

  • IfBlock — {#if}...{:else if}...{:else}...{/if}
  • EachBlock — {#each}...{:else}...{/each}
  • AwaitBlock — {#await}...{:then}...{:catch}...{/await}
  • KeyBlock — {#key}...{/key}
  • SnippetBlock — {#snippet name()}...{/snippet}

Attribute nodes:

  • Attribute — regular HTML attribute
  • SpreadAttribute — {...spread} props spread
  • BindDirective — bind:value
  • ClassDirective — class:active={...}
  • StyleDirective — style:color={...}
  • EventHandler — on:click={handler} (legacy) or onclick={handler}
  • TransitionDirective — transition:fade
  • AnimateDirective — animate:flip
  • UseDirective — use:action
  • LetDirective — let:slot (legacy slots)
  • AttachDirective — {@attach}

8.6 Key Utility Classes

CompileDiagnostic (packages/svelte/src/compiler/utils/compile_diagnostic.js:51):

The base class for all compiler errors and warnings. Extends Error with:

  • code — machine-readable error code (e.g., invalid_rune_usage)
  • start / end — source position ({ line, column, character })
  • filename — source file path
  • URL to svelte.dev/e/{code} or svelte.dev/w/{code} for documentation

InternalCompileError extends CompileDiagnostic for compiler bugs (not user errors).

MappedCode (packages/svelte/src/compiler/utils/mapped_code.js:75):

Represents a code string with an associated source map. Used during code generation to accumulate transformed code while maintaining position mapping back to source. Built on magic-string.

FuzzySet (packages/svelte/src/compiler/phases/1-parse/utils/fuzzymatch.js:124):

A fuzzy string matching data structure used for "did you mean X?" suggestions in error messages. When the user writes $stat instead of $state, FuzzySet finds the closest matching rune name.

8.7 Error System Architecture

Compiler errors follow a structured pattern:

// In errors.js (generated)
export function invalid_rune_usage(node, rune) {
  e(node, "invalid_rune_usage", `...message...\nhttps://svelte.dev/e/invalid_rune_usage`);
}

// Usage in analyzer
import * as e from '../errors.js';
e.invalid_rune_usage(node, '$state');

Error codes are organized by category:

Prefix Category
options_* Compiler option validation
rune_* Rune usage errors
props_* Props-related errors
state_* State-related errors
bind_* Binding errors
css_* CSS errors
block_* Block syntax errors
animation_* Animation directive errors
attribute_* Attribute errors
snippet_* Snippet errors
legacy_* Legacy Svelte 4 feature errors in Svelte 5 mode
typescript_* TypeScript-specific errors

8.8 Experimental Flags

Experimental features are controlled via flag modules in packages/svelte/src/internal/flags/:

Flag module Feature Status
async.js Async component support ({#await} improvements, async snippets) Experimental
legacy.js Legacy Svelte 4 compatibility behaviors Stable (used by legacy option)
tracing.js Effect dependency tracing ($inspect.trace) Dev-mode feature

These flags can be toggled at the application entry point before any Svelte components are imported:

// Enable experimental async support
import { set_async_mode_flag } from 'svelte/internal/flags/async';
set_async_mode_flag(true);

IMPORTANT: Experimental flags are subject to breaking changes without semver consideration. Do not use in production without accepting this risk.

8.9 Ignorable Runtime Warnings

The following runtime warnings are commonly suppressed in production or specific development scenarios. They indicate non-critical conditions:

Warning code Description When to suppress
await_waterfall Nested {#await} blocks create a waterfall of requests When waterfall is intentional
await_reactivity_loss Promise inside {#await} is not reactive When using non-reactive promises intentionally
state_snapshot_uncloneable $state.snapshot() found an uncloneable value When snapshots include complex objects
binding_property_non_reactive Binding targets a property that isn't reactive When binding to computed/external properties
hydration_attribute_changed Attribute differs between SSR and client When difference is intentional (e.g., random IDs)
hydration_html_changed Raw HTML differs between SSR and client When SSR and client content legitimately differ
ownership_invalid_binding A component binds to a prop it doesn't own When intentional cross-component binding is needed
ownership_invalid_mutation A component mutates state it doesn't own When intentional shared state mutation is needed

Suppress specific warnings in svelte.config.js or via the onwarn plugin option:

// svelte.config.js
export default {
  compilerOptions: {
    // warnings are suppressed per-component via svelte-ignore comment
  },
};
<!-- Suppress in a specific file -->
<!-- svelte-ignore ownership_invalid_mutation -->
{parent.value = 'changed'}

8.10 Contributing Guide

Repository setup:

git clone https://github.com/sveltejs/svelte
cd svelte
pnpm install        # install all workspace dependencies
pnpm build          # initial build (required before tests)

Development workflow:

cd packages/svelte
pnpm dev            # Rollup watch mode — rebuilds on source changes

# In another terminal
pnpm test           # Run tests (watches on source changes with Vitest)

Making a change:

  1. Edit source in packages/svelte/src/
  2. Build: pnpm build (or watch mode handles it)
  3. Add/update tests in packages/svelte/tests/
  4. Update snapshots if needed: pnpm test -- --update-snapshots
  5. Add a changeset: pnpm changeset (required for all user-visible changes)
  6. Run linting: pnpm lint && pnpm check

Test fixture pattern:

Most test categories use input/output fixture directories:

tests/runtime/samples/my-test-case/
  main.svelte    ← input component
  _config.js     ← test configuration (expected behavior)
tests/css/samples/basic/
  input.svelte   ← input component
  output.css     ← expected CSS output

Compiler error tests:

tests/validator/samples/rune-invalid-location/
  input.svelte   ← input that should produce an error
  errors.json    ← expected error codes and messages

9. Integration Points

9.1 Vite Plugin Integration

@sveltejs/vite-plugin-svelte is the primary integration layer between Svelte and Vite-based toolchains.

Plugin lifecycle:

  1. transform hook — intercepts .svelte and .svelte.js files
    • Runs preprocessors (if configured)
    • Calls compile() or compileModule() with the preprocessed source
    • Returns { code, map } for Vite's module graph
  2. handleHotUpdate hook — manages HMR
    • Re-compiles changed .svelte files
    • Injects HMR update payload using Svelte's HMR API
    • Invalidates dependent modules in Vite's module graph
  3. CSS extraction — the plugin separates CSS from the JS output and registers it as a virtual module (?svelte&type=style)

Plugin configuration is detailed in §3.7.

9.2 Rollup Plugin Integration

For non-Vite Rollup builds, use rollup-plugin-svelte:

// rollup.config.js
import svelte from 'rollup-plugin-svelte';
import { nodeResolve } from '@rollup/plugin-node-resolve';
import css from 'rollup-plugin-css-only';

export default {
  input: 'src/main.js',
  output: { file: 'public/bundle.js', format: 'iife' },
  plugins: [
    svelte({
      compilerOptions: { dev: !production },
      preprocess: sveltePreprocess(),
    }),
    css({ output: 'bundle.css' }),
    nodeResolve({ browser: true, exportConditions: ['browser'] }),
  ],
};

IMPORTANT: When resolving Svelte's package, ensure the browser export condition is set. Without it, the server runtime is loaded in browser bundles. In Rollup: exportConditions: ['browser']. In Webpack: set resolve.conditionNames.

9.3 Preprocessors

Preprocessors transform .svelte source before the Svelte compiler sees it. They enable TypeScript, SCSS, PostCSS, and other language transformations.

Preprocessor API contract:

interface PreprocessorGroup {
  name?: string;

  markup?: (options: {
    content: string;
    filename?: string;
  }) => Processed | Promise<Processed>;

  script?: (options: {
    content: string;
    markup: string;
    attributes: Record<string, string | boolean>;
    filename?: string;
  }) => Processed | Promise<Processed>;

  style?: (options: {
    content: string;
    markup: string;
    attributes: Record<string, string | boolean>;
    filename?: string;
  }) => Processed | Promise<Processed>;
}

interface Processed {
  code: string;
  map?: string | object;  // source map
  dependencies?: string[];  // files to watch
  attributes?: Record<string, string | boolean>;  // updated attributes
}

Multiple preprocessors are applied in sequence. Each receives the output of the previous:

preprocess([
  vitePreprocess(),          // TypeScript, SCSS
  myCustomPreprocessor(),    // custom transformation
])

9.4 Store Interoperability

svelte/store provides the store contract used by many Svelte libraries. Any object implementing this contract is "store-compatible":

interface Readable<T> {
  subscribe(run: (value: T) => void, invalidate?: () => void): () => void;
}

interface Writable<T> extends Readable<T> {
  set(value: T): void;
  update(updater: (value: T) => T): void;
}

Stores and runes interop: In Svelte 5 runes mode, stores are auto-subscribed with $store prefix syntax (Svelte 4 behavior). Alternatively, use $state with manual subscription:

// Adapting an external store to $state
import { myStore } from './external.js';
import { onDestroy } from 'svelte';

let value = $state(undefined);
const unsubscribe = myStore.subscribe(v => { value = v; });
onDestroy(unsubscribe);

9.5 svelte/reactivity — Standalone Reactivity

The svelte/reactivity package exports reactive primitives for use outside of Svelte components:

import { SvelteMap, SvelteSet, SvelteURL, SvelteDate } from 'svelte/reactivity';
import { MediaQuery } from 'svelte/reactivity/window';

// Reactive Map — mutations trigger updates
const map = new SvelteMap();
map.set('key', 'value');

// Reactive window media query
const isSmall = new MediaQuery('(max-width: 600px)');

These are subclasses of their standard counterparts with Svelte's reactivity wired in.

9.6 svelte/attachments — DOM Attachments

The {@attach} directive and svelte/attachments package provide a composable alternative to use: actions:

import { on, createAttachmentKey } from 'svelte/attachments';

// Create a reusable attachment
const tooltip = createAttachmentKey();

// Use in template
// <div {@attach tooltip}>...</div>

Attachments are functions that receive the DOM element and return an optional cleanup function. They compose naturally with the @attach attachment syntax.

9.7 TypeScript Integration

Svelte provides full TypeScript support:

Component typing:

// MyComponent.svelte — TypeScript in <script lang="ts">
<script lang="ts">
  interface Props {
    name: string;
    count?: number;
  }
  
  const { name, count = 0 }: Props = $props();
</script>

Generic components:

<script lang="ts" generics="T extends { id: string }">
  const { items }: { items: T[] } = $props();
</script>

SvelteComponent base type (for dynamic <svelte:component>):

import type { SvelteComponent, ComponentType } from 'svelte';

let component: ComponentType<SvelteComponent<{ name: string }>> = MyComponent;

SvelteComponentTyped<Props, Events, Slots> is the Svelte 4 typed base class, preserved for compatibility in svelte/legacy. In Svelte 5, components are typed via their props interface directly.

Type generation: dts-buddy generates .d.ts files from the Svelte package source during the build (pnpm generate:types). This produces accurate types for all exported APIs including the compiler.


Known Issues & Active Bugs

Based on open GitHub issues at source commit fcaa8ce:

Issue Title Affected area Status
#17027 lifecycle_outside_component in SSR builds SSR / lifecycle Open
#18095 $effect.pending() broken with $state.eager Reactivity Open
#18096 Detached DOM subtrees retained after destroy Memory / DOM Open
#18120 Keyed each worse performance than non-keyed Performance Open
#8089 a11y mouse events warning with focusin/focusout A11y warnings Open
#17327 video muted attribute not rendering in SSR SSR attributes Open
#18119 @const referencing let slot prop regression Compiler Open
#18097 Spurious derived_inert warnings Reactivity warnings Open
#18103 Memory leak with $effect + parent prop Memory Open
#18094 DOM leak on unresolved promises Memory / DOM Open
#18092 Style hashing with filename causes leaks CSS / Memory Open

NOTE: These issues were open at the time of document generation. Check the Svelte GitHub repository for current status.


Quick Reference

Key File Locations

What Where
Compiler entry packages/svelte/src/compiler/index.js
Runtime client entry packages/svelte/src/internal/client/runtime.js
Runtime server entry packages/svelte/src/internal/server/index.js
All bitflag constants packages/svelte/src/constants.js
All utility functions packages/svelte/src/utils.js
Public type definitions packages/svelte/src/index.d.ts
Package exports map packages/svelte/package.json (exports field)
Rollup build config packages/svelte/rollup.config.js
Monorepo workspace pnpm-workspace.yaml

Dependency Summary

Dependency Role Type
acorn JavaScript parser Runtime
@sveltejs/acorn-typescript TypeScript extension for acorn Runtime
magic-string String manipulation with source maps Runtime
@jridgewell/remapping Source map composition Runtime
@jridgewell/sourcemap-codec Source map encode/decode Runtime
zimmerframe AST walking utility Runtime
esrap AST → JS code printer Runtime
is-reference Check if AST node is a reference Runtime
locate-character Position lookup in strings Runtime
aria-query ARIA role/attribute database Runtime
axobject-query AXObject accessibility database Runtime
clsx Class name utility Runtime
devalue Serialize complex JS values (SSR) Runtime
esm-env BROWSER/SERVER constants Runtime
rollup Compiler bundle build Dev
vitest Unit testing Dev
@playwright/test E2E testing Dev
dts-buddy TypeScript declaration generation Dev
typescript Type checking Dev