sveltejs/svelte
Legacy compatibility
Active contributors: Rich Harris, Simon H
Purpose
Svelte 5 ships full backward compatibility with Svelte 3/4 components. A component without runes runs in legacy mode with the original semantics: export let prop, $: reactive labels, auto-subscribed stores ($count), createEventDispatcher, and slot= named slots.
This page collects the legacy machinery so contributors know where each piece lives.
Legacy detection
Mode detection happens during analysis in packages/svelte/src/compiler/phases/2-analyze/index.js. A component is legacy unless:
- it uses any rune (
$state,$derived, etc.), or <svelte:options runes>is set, or- the compile option
runes: trueis passed.
In legacy mode, the analyzer treats let foo = ... at instance-level as reactive in the Svelte 4 sense (mutations re-render), and $: y = ... as a reactive declaration.
legacy_mode_flag
packages/svelte/src/internal/flags/legacy.js exposes a legacy_mode_flag. When the compiler emits a legacy-mode component, it imports this flag, which switches the runtime onto compatibility paths in:
packages/svelte/src/internal/client/legacy.js— captured signals tracking for$:.packages/svelte/src/internal/client/dom/legacy/—init.js,lifecycle.js,event-modifiers.js,misc.js(legacy event listener bubbling, props update propagation).packages/svelte/src/legacy/legacy-client.jsandlegacy-server.js— the Svelte 4 component class shim (createClassComponent).
Legacy public API
packages/svelte/src/legacy/legacy-client.js (8 KB) and 4 KB) export legacy-server.js (createClassComponent and other helpers that wrap a Svelte 5 component as a Svelte 4-style class with .$set, .$on, .$destroy, etc. This is what @sveltejs/svelte-virtual-list and other libraries built against Svelte 4 use unchanged.
Auto-subscribe stores
The $store shorthand auto-subscribes to a store while a reactive scope is active. The runtime piece is packages/svelte/src/internal/client/reactivity/store.js. The compiler emits store_get(store, '$store') in legacy mode. In runes mode the same shorthand is an error and you must use fromStore(store) instead.
Reactive declarations ($:)
A $: label rewrites to a legacy_pre_effect (or legacy_pre_effect_reset for reset semantics) at the instance level. Implementation in internal/client/reactivity/effects.js. The dependency tracking is the same Reaction graph used by runes — the legacy flow just opts into a different scheduling order.
createEventDispatcher
createEventDispatcher is exported from packages/svelte/src/index-client.js and is marked deprecated. It walks component_context.s.$$events and dispatches a synthetic CustomEvent to each listener. Replacement: callback props or $host().
Migration codemod
The Svelte 4 → 5 codemod is packages/svelte/src/compiler/migrate/index.js (~2k lines). Exposed as svelte/compiler#migrate. It rewrites:
export let foo→let { foo } = $props().$: y = expr→let y = $derived(expr)when safe.$: { ... }→$effect(() => { ... })when safe.- Inline
$storereads →fromStore(store).currentwhen needed. createEventDispatchercalls → callback prop suggestions (with manual review nudge).
The migration tool re-uses the parser, scope analyzer, and magic-string to produce a textual rewrite.
Tests
packages/svelte/tests/runtime-legacy/exercises the compatibility runtime.packages/svelte/tests/migrate/covers the codemod with_input.svelte/_output.sveltepairs.- Top-level
pnpm test runtime-legacyis the canonical filter.
Key source files
| File | Purpose |
|---|---|
packages/svelte/src/internal/flags/legacy.js |
legacy_mode_flag setter. |
packages/svelte/src/internal/client/legacy.js |
Captured-signals tracking for legacy $:. |
packages/svelte/src/internal/client/dom/legacy/init.js |
Legacy component init (lifecycle ordering). |
packages/svelte/src/internal/client/dom/legacy/lifecycle.js |
beforeUpdate / afterUpdate integration. |
packages/svelte/src/internal/client/dom/legacy/event-modifiers.js |
once, preventDefault, self, stopImmediatePropagation, stopPropagation, trusted. |
packages/svelte/src/internal/client/reactivity/store.js |
Auto-subscribe store helpers. |
packages/svelte/src/legacy/legacy-client.js |
createClassComponent. |
packages/svelte/src/compiler/migrate/index.js |
Svelte 4 → 5 migration codemod. |
Entry points for modification
Legacy support is intentionally frozen. Bug fixes go into the relevant compatibility file. New features should not depend on legacy_mode_flag — runes mode is the default Svelte 5 surface. The migration codemod is the most-touched file in this set; if you find a Svelte 4 idiom it doesn't handle, add a sample under packages/svelte/tests/migrate/samples/ and extend migrate/index.js.
Built by Factory AutoWiki from public repository content. It is a generated preview for codebase exploration, not source-maintained documentation.