SuperAPI AI Directory

svelte-legacy-apis

Coding & development

View repository

Updated 10-15 21:48

About this skill

Svelte Legacy APIs Reference (Svelte 4 → Svelte 5)

This skill covers Svelte 4's Legacy API, which is still usable in Svelte 5 (Legacy Mode), but migrating to Runes is recommended. This skill is only for understanding and migrating Legacy code,new code should use Runes。

When to use this skill

Use this skill when the user needs to maintain a Svelte 4 project, understand $: reactive statements,export let props、createEventDispatcher, Slots, or needs to migrate Legacy code to Svelte 5 Runes Mode.

Critical: Reactive let declarations

In Legacy Mode (components not using Runes), top-level let declarations automatically become reactive:

<script>
  let count = 0; // 自动响应式
</script>

<button on:click={() => count++}> {count}


> Svelte 5 的 Runes Mode:`let count = $state(0)`。

### 数组/对象变更注意

Legacy 响应式基于**赋值**,数组方法(`.push()`、`.splice()`)不触发更新:

```svelte
<script>
  let items = [1, 2, 3];

  function addItem() {
    items.push(4);    // ❌ 不触发更新
    items = items;    // ✅ 触发
    // 或
    items = [...items, 4]; // ✅ 触发
  }
</script>

Critical: Reactive $: statements

In Legacy Mode,$: labeled statements create reactive statements:

<script>
  let a = 1;
  let b = 2;

  $: sum = a + b;              // 依赖 a, b
  $: console.log(`sum = ${sum}`); // 依赖 sum
</script>

Topological sorting

$: statements are topologically sorted by dependency (order-independent):

$: z = y;    // z 依赖 y
$: y = x;    // y 依赖 x
$: x = 5;    // x 无依赖

Block-level statements

$: {
  total = 0;
  for (const item of items) total += item.value;
}

Critical: export let

In Legacy Mode, declare props with export let :

<script>
  export let name;              // 必需 prop
  export let age = 18;          // 带默认值
  export let items = [];         // 数组默认值
</script>

Svelte 5 Runes:let { name, age = 18 } = $props()

Re-export (API)

<script>
  export function greet(name) {
    alert(`Hello ${name}!`);
  }
  export const VERSION = '1.0';
</script>

The parent component calls via bind:this :

<script>
  let greeter;
</script>
<Greeter bind:this={greeter} />
<button on:click={() => greeter.greet('world')}>greet</button>

prop renaming

<script>
  export { foo as bar }; // 对外叫 bar,内部叫 foo
</script>

Critical: $$props / $$restProps

$$props (get all passed-in props)

<script>
  let { foo, ...rest } = $$props;
  // foo 是显式 prop,rest 是其余所有 props
</script>

Svelte 5 Runes:let { foo, ...rest } = $props()

$$restProps (only the remaining props)

<script>
  export let required;
  let { ...rest } = $$props;
</script>
<!-- required 不在 rest 中 -->

Critical: createEventDispatcher

Legacy's event dispatch mechanism (replaced by Svelte 5 callback props):

<script>
  import { createEventDispatcher } from 'svelte';

  const dispatch = createEventDispatcher();

  function handleClick() {
    dispatch('change', { value: 42 });
    dispatch('complete'); // 无数据
  }
</script>

<button on:click={handleClick}>发送</button>

Using events

<Component on:change={(e) => console.log(e.detail)} />

Typed (TypeScript)

<script lang="ts">
  const dispatch = createEventDispatcher<{
    change: { value: number };
    complete: null;
  }>();
</script>

Svelte 5 recommends: use callback props directly: <Component onChange={(detail) => handle(detail)} />

Critical: Slots

<slot> is Legacy's content-passing mechanism (Svelte 5 replaces it with Snippets):

Default slot

<!-- Component.svelte -->
<div class="card">
  <slot />
</div>

<!-- 使用 -->
<Component>
  <p>内容</p>
</Component>

Named slot

<!-- Component.svelte -->
<slot name="footer" />
<slot name="header" />

<!-- 使用 -->
<Component>
  <p slot="header">头部</p>
  <p slot="footer">尾部</p>
</Component>

Slot props (passing data to slot)

<!-- Component.svelte -->
<slot value={count} onClick={handleClick} />

<!-- 使用 -->
<Component let:value let:onClick>
  <p>{value}</p>
  <button on:click={onClick}>click</button>
</Component>

Note: In Svelte 5, default slot props require the let: prefix; named slots' slot props are not exposed to named slots.

$$slots (check whether a slot exists)

<script>
  import { createEventDispatcher } from 'svelte';
  const dispatch = createEventDispatcher();
  $: if (!$$slots.footer) {
    // footer slot 未被提供
  }
</script>

Critical: svelte:component / svelte:self

<svelte:component>(dynamic components)

<script>
  let Current = FirstComponent;
  $: Current = showSecond ? SecondComponent : FirstComponent;
</script>

<svelte:component this={Current} />

Svelte 5 recommends:<svelte:element this={Current} />

<svelte:self>(recursive components)

<script>
  import { svelte:self } from 'svelte';
</script>

<svelte:self />

Critical: svelte:fragment

<!-- Component.svelte -->
<slot name="title" />
<div>
  <slot name="body" />
</div>

<!-- 使用 -->
<Component>
  <h1 slot="title">Title</h1>
  <svelte:fragment slot="body">
    <p>多元素</p>
    <p>不会被包裹</p>
  </svelte:fragment>
</Component>

Critical: v5 Migration Quick Reference

Legacy (Svelte 4) Modern (Svelte 5)
let count = 0(top-level reactive) let count = $state(0)
$: x = expr let x = $derived(expr)
$: { if (cond) x = v } $effect(() => { if (cond) x = v })
export let prop let { prop } = $props()
$$props let { ...rest } = $props()
<slot /> {@render children()}
createEventDispatcher callback props
on:click={fn} onclick={fn}
<svelte:component this={x}> <svelte:element this={x}>
<svelte:self> import Self from './This.svelte' + <Self>
class:cool={isCool} class={{ cool: isCool }}
let:slotProp={value} Snippets automatically passed

Gotchas

  1. Legacy $: is assignment-based — array .push() and other methods do not trigger updates
  2. Slot props behave differently — default slot can access slot props, named slots cannot
  3. createEventDispatcher is deprecated — not recommended in Svelte 5, new code should use callback props
  4. $$slots is injected at compile time — can be used directly in script $: reference
  5. <svelte:self> requires import — import { svelte:self } from 'svelte'

Critical: Svelte 4 Migration (Svelte 3 → Svelte 4)

If you are maintaining a Svelte 3 project and upgrading to Svelte 4, the following breaking changes must be checked item by item. For detailed comparison and automated scripts, see examples/migration-v4.md and references/migration-v4-reference.md, source baseline at /tmp/svelte-llms.txt:6182。

Recommended starting point:npx svelte-migrate@latest svelte-4 — the script automatically handles most items; the rest require manual intervention.

Minimum version requirements

{
  "engines": { "node": ">=16" },
  "devDependencies": {
    "svelte": "^4.0.0",
    "@sveltejs/kit": ">=1.20.4",
    "vite-plugin-svelte": ">=2.4.1",
    "svelte-loader": ">=3.1.8",
    "rollup-plugin-svelte": ">=7.1.5",
    "typescript": ">=5"
  }
}

Major breaking changes quick reference

Category Svelte 3 Svelte 4
Bundler browser condition Optional Rollup browser: true / Webpack conditionNames: ['browser']
CJS output Support Removed (compiler,svelte/register、runtime)
createEventDispatcher type Optional payload Strict:T | undefined optional;T required;null disable detail
Action / ActionReturn params implicit any must be explicit Action<HTMLElement, T>
onMount async cleanup no error A cleanup function that returns a Promise is ignored (TS error)
Custom Element <svelte:options tag="x" /> <svelte:options customElement="x" />(string or object)
SvelteComponentTyped with SvelteComponent coexist deprecated, unified as SvelteComponent
Transitions default scope Any ancestor block triggers Only the direct parent block triggers; add |global to restore the old behavior
Default slot bindings Named slots are also visible Only the default slot is visible
Preprocessor order markup×N, script×N, style×N markup, script, style ×N (three segments inside each preprocessor)
eslint-plugin-svelte3 Recommended Deprecated, change to eslint-plugin-svelte
inert on outroing Not applied Applied by default (accessibility improvement)
classList.toggle Compatible with old browsers Requires a polyfill
CustomEvent Compatible with old browsers Requires a polyfill
derived falsy values Warning Throw
svelte/internal types Exposed Removed
svelte.JSX namespace Use Change to svelteHTML / svelte/elements
Library authors peerDependencies ^3 ^3 || ^4

What automation scripts can handle

  • tag= → customElement=(string form)
  • SvelteComponentTyped → SvelteComponent
  • Action / ActionReturn generic parameter
  • Transitions default behavior (add the |global modifier)

What needs to be handled manually

  • Bundler browser condition configuration (Rollup / Webpack)
  • Fixes for strict type errors (createEventDispatcher payload)
  • onMount async cleanup changed to synchronous
  • Preprocessor name attributes + order adjustments
  • ESLint plugin replacement + config migration
  • derived falsy value edge cases

Custom Element upgrade

<!-- v3 --><svelte:options tag="my-counter" />
<!-- v4 --><svelte:options customElement={{ tag: 'my-counter', props: { initial: { type: Number } } }} />

onMount async cleanup anti-pattern

// ❌ v4 报错且永远不清理
onMount(async () => { const data = await fetch('/api'); setup(data); return () => teardown(); });
// ✅ 同步返回值
onMount(() => { fetch('/api').then(setup); return () => teardown(); });

Preprocessor order: MDsveX must come first

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

export default {
  preprocess: [
    mdsvex({ name: 'mdsvex' }),         // 先 markup 再 script
    vitePreprocess({ name: 'vite' })
  ]
};

v3: all markup → all script → all style. v4: inside each preprocessor, markup → script → style, then the next one. Each preprocessor must declare name。

Library authors' peerDependencies

{
  "peerDependencies": { "svelte": "^3.0.0 || ^4.0.0" },
  "devDependencies": { "svelte": "^4.0.0" }
}

Run-it-Yourself Checklist

npm install svelte@^4                    # 1. 升级依赖
npx svelte-migrate@latest svelte-4       # 2. 跑迁移脚本
npx svelte-check                          # 3. 看 TypeScript
npm test                                   # 4. 跑测试

If many Action generic errors appear, it means the migration script missed them; manually add <HTMLElement, YourType>。

Further v5 migration is in svelte-runes / svelte-misc; this skill only covers the Svelte 4 upgrade.

Quick Fixes

Problem Solution
Array .push() not updating items = [...items, newItem] or items = items
slot styles not taking effect Style isolation is scoped and does not affect slot content
$$restProps type error Confirm it is export let and not let
EventDispatcher type issue Upgrade TypeScript to 5+

FAQ

Q: When must the Legacy API be used? A: When maintaining an un-migrated Svelte 4 project. New projects should use Runes.

Q: Can Legacy and Runes be mixed? A: Yes. Once a component uses $state , it enters Runes Mode, and the parts not using it use Legacy syntax.

Q: $: and $derived ? A: The functionality is similar, but $derived is Svelte 5's Runes API, which is more explicit and controllable.$: is available in Legacy Mode.

Q: Which is better, Slot or Snippet? A: Snippets are more powerful and flexible. Slot is suitable for simple content passing; Snippet can accept parameters, can be used where it is defined, and has better type safety.

Cross-References

This skill is dedicated to Svelte Legacy maintenance, and works in coordination with the following skills:

  • Svelte 5 entry and learning path → hand off to svelte-awesome skill. Install: npx skills add full-stack-skills/svelte-skills --skill svelte-awesome.
  • Svelte 4 → Svelte 5 migration comparison → hand off to svelte-misc skill (migration section). Install: npx skills add full-stack-skills/svelte-skills --skill svelte-misc.
  • Runes reactivity ($state / $derived / $effect)→ hand off to svelte-runes skill. Install: npx skills add full-stack-skills/svelte-skills --skill svelte-runes.
  • Stores(writable / readable / derived)→ hand off to svelte-lifecycle skill. Install: npx skills add full-stack-skills/svelte-skills --skill svelte-lifecycle.
  • Template syntax (snippet / event / bind) → hand off to svelte-template-syntax skill. Install: npx skills add full-stack-skills/svelte-skills --skill svelte-template-syntax.
  • Debugging reactivity not updating / $inspect → hand off to svelte-lifecycle skill. Install: npx skills add full-stack-skills/svelte-skills --skill svelte-lifecycle.
  • Compile components as Web Components → hand off to svelte-misc skill (Custom Elements section). Install: npx skills add full-stack-skills/svelte-skills --skill svelte-misc.
  • Initialization and migration scripts → hand off to svelte-cli skill(sv migrate svelte-5). Install: npx skills add full-stack-skills/svelte-skills --skill svelte-cli.

Examples

Practical examples for working with Legacy APIs:

References

Complete reference documentation: