Open-Source Wikis

/

Svelte

/

Packages

/

Client runtime

sveltejs/svelte

Client runtime

Active contributors: Rich Harris, Simon H, Dominic Gannaway

Purpose

The client runtime is the JavaScript that compiled .svelte components import to mount, hydrate, react, and update the DOM. It lives entirely under packages/svelte/src/internal/client/ and is exposed to compiled code as svelte/internal/client (subpath defined in packages/svelte/package.json).

User-facing entries — mount, hydrate, unmount, tick, untrack, flushSync, getContext, onMount, etc. — are re-exported from packages/svelte/src/index-client.js.

Directory layout

src/internal/client/
├── render.js                # mount / hydrate / unmount entry points
├── runtime.js               # active reaction tracking, update_effect
├── context.js               # ComponentContext, push/pop
├── reactivity/
│   ├── sources.js           # leaf reactive values
│   ├── deriveds.js          # memoised reactions
│   ├── effects.js           # side-effectful reactions
│   ├── batch.js             # microtask scheduler, flushSync, fork
│   ├── async.js             # async reaction support
│   ├── store.js             # store ↔ reactivity bridge
│   ├── props.js             # $props, $bindable plumbing
│   ├── equality.js, status.js, types.d.ts, utils.js
├── proxy.js                 # deep-reactive Proxy for $state(object)
├── error-handling.js        # boundary integration
├── dom/
│   ├── operations.js        # createElement, append, etc. wrappers
│   ├── template.js          # parsed template caching
│   ├── reconciler.js        # task scheduler
│   ├── task.js              # rAF/microtask helper
│   ├── hydration.js         # cursor-based hydration walker
│   ├── css.js               # scoped style insertion
│   ├── blocks/              # one file per block kind (if/each/await/...)
│   ├── elements/            # actions, attachments, attributes, bindings, events, transitions
│   └── legacy/              # Svelte 4 lifecycle + event modifiers
├── dev/                     # dev-only helpers (HMR, tracing, $inspect, ownership, ...)
├── flags/, hydratable.js, legacy.js, loop.js, timing.js
└── constants.js             # bitmask flags (CLEAN, DIRTY, DERIVED, ...)

Mount, hydrate, unmount

packages/svelte/src/internal/client/render.js exports the three main lifecycle entry points. They share an internal _mount helper that:

  1. Initialises low-level DOM operations (init_operations from dom/operations.js).
  2. Creates a fresh ComponentContext (context.js) and pushes it onto the stack.
  3. Runs the compiled component function inside a component_root effect (reactivity/effects.js).
  4. Returns the component's exports object.

hydrate flips a global hydrating flag and walks server-rendered comment markers (HYDRATION_START / HYDRATION_END) to attach reactivity without re-creating the DOM. The walker primitives live in dom/hydration.js.

unmount tears down the root effect, which recursively destroys child blocks, runs teardown callbacks, and removes event listeners.

Blocks

Each block in template syntax compiles to a runtime call. The block runtime functions live one-per-file under dom/blocks/:

Block syntax File
{#if} blocks/if.js
{#each} blocks/each.js (~18 KB, the largest)
{#await} blocks/await.js
{#key} blocks/key.js
{#snippet} / {@render} blocks/snippet.js
{@html} blocks/html.js
{@async} blocks/async.js
<svelte:component> blocks/svelte-component.js
<svelte:element> blocks/svelte-element.js
<svelte:head> blocks/svelte-head.js
<svelte:boundary> blocks/boundary.js (~13 KB)
<slot> blocks/slot.js
CSS custom properties blocks/css-props.js
branches blocks/branches.js

Element-level helpers

Generated DOM updates rely on helpers under dom/elements/:

  • Attributes: attributes.js (~20 KB) handles set_attribute, boolean attribute correction, hydration mismatches, and the class/style directives. class.js and style.js are companions.
  • Events: events.js (~11 KB) implements event delegation, modifier wrapping (legacy/event-modifiers.js), and add_event_listener.
  • Bindings: bindings/ is the largest sub-tree — input, media, navigator (bind:online), props, select, size (bind:contentRect), this, universal (bind:innerHTML), and window scroll/size bindings. Each one creates a reciprocal Source ↔ DOM listener.
  • Transitions: transitions.js (~14 KB) coordinates intro/outro transitions and the animate: directive. See Transitions.
  • Actions / attachments: actions.js (legacy use:) and attachments.js (modern {@attach}).
  • Custom elements: custom-element.js (~10 KB) implements <svelte:options customElement>.
  • Customizable select: customizable-select.js is the polyfill helpers for <selectedcontent>.

Reactivity primitives

See Reactivity engine for the deep dive. The five files that matter are:

File What it owns
internal/client/runtime.js active_reaction, update_effect, get, dependency tracking.
internal/client/reactivity/sources.js state(...), set(...), increment(...), flush_eager_effects.
internal/client/reactivity/deriveds.js derived(...), recompute logic, freeze/unfreeze.
internal/client/reactivity/effects.js effect(...), branch(...), teardown semantics.
internal/client/reactivity/batch.js Batch, flushSync, fork, scheduling.

Public exports

packages/svelte/src/index-client.js is what user code imports as svelte:

export { flushSync, fork } from './internal/client/reactivity/batch.js';
export {
  createContext,
  getContext,
  getAllContexts,
  hasContext,
  setContext,
} from './internal/client/context.js';
export { hydratable } from './internal/client/hydratable.js';
export { hydrate, mount, unmount } from './internal/client/render.js';
export { tick, untrack, settled } from './internal/client/runtime.js';
export { createRawSnippet } from './internal/client/dom/blocks/snippet.js';
// + getAbortSignal, onMount, onDestroy, createEventDispatcher, beforeUpdate, afterUpdate (deprecated)

The DEV builds also install global getters for $state, $effect, $derived, $inspect, $props, $bindable that throw rune_outside_svelte errors if accessed outside a component context — see the top of index-client.js.

Dev-only helpers

internal/client/dev/ bundles features that are only included in DEV builds via import { DEV } from 'esm-env':

  • tracing.js$inspect.trace integration, captures stack traces for each reaction update.
  • hmr.js — hot module replacement marker class and re-mount logic.
  • inspect.js$inspect(value) log integration.
  • ownership.js — warns when a component mutates state owned by another component.
  • legacy.js, validation.js, assign.js, css.js, elements.js — additional dev-only checks.

These are listed in internal/client/index.js so the compiler can address them by name.

Key source files

File Purpose
packages/svelte/src/internal/client/render.js mount, hydrate, unmount plus set_text.
packages/svelte/src/internal/client/runtime.js Reaction tracking, scheduling glue.
packages/svelte/src/internal/client/context.js ComponentContext, push/pop, context API.
packages/svelte/src/internal/client/proxy.js Deep-reactive Proxy.
packages/svelte/src/internal/client/dom/blocks/each.js The keyed/non-keyed list reconciler.
packages/svelte/src/internal/client/dom/elements/attributes.js Attribute setting, hydration mismatch repair.
packages/svelte/src/internal/client/dom/elements/events.js Event delegation, modifier handling.
packages/svelte/src/internal/client/error-handling.js <svelte:boundary> integration.

Entry points for modification

To add a new template block, you need: a new visitor under phases/3-transform/client/visitors/, a new file under internal/client/dom/blocks/, and an export in internal/client/index.js. To add a new binding, add a file under dom/elements/bindings/ and a bind_* export. To change reactivity semantics, the four places to look are runtime.js, reactivity/effects.js, reactivity/sources.js, and reactivity/batch.js.

Built by Factory AutoWiki from public repository content. It is a generated preview for codebase exploration, not source-maintained documentation.

Client runtime – Svelte wiki | Factory