SuperAPI AI Directory

svelte-misc

Frontend & websites

View repository

Updated 10-16 16:58

About this skill

Svelte Misc Reference (Svelte 5)

This skill covers Svelte 5's advanced TypeScript support, custom elements (Web Components), browser compatibility matrix, Svelte 4→5 migration guide, official best practices, and common Q&A.

When to use this skill

Use this skill when the user needs TypeScript type annotations (generics, wrapper components, DOM type augmentation), to create custom Web components and use $host() / $bindable / extend, to view the browser compatibility matrix and feature exceptions, to migrate from Svelte 4 to Svelte 5, or to follow the Svelte 5 recommended $state / $derived / $effect patterns.


Critical: TypeScript advanced

Svelte natively supports TypeScript (no configuration needed for Vite projects). Use <script lang="ts"> to enable type annotations; runtime features such as enums / parameter properties require a preprocessor.

<script lang="ts">
  let name: string = 'world';
  function greet(name: string) { alert(`Hello, ${name}!`); }
</script>
<button onclick={(e: Event) => greet((e.target as HTMLElement).innerText)}>
  {name}
</button>

lang="ts" by default only supports "types disappear" features (annotations, interfaces, generics). Runtime code features (enum,private foo = 1 parameter properties in the form) require configuring vitePreprocess({ script: true })。

Typed Props + passthrough

<script lang="ts">
  import type { Snippet } from 'svelte';
  interface Props {
    requiredProperty: number;
    optionalProperty?: boolean;
    snippetWithStringArgument: Snippet<[string]>;
    eventHandler: (arg: string) => void;
    [key: string]: unknown; // 配合 ...rest 透传其他属性
  }
  let { requiredProperty, optionalProperty, snippetWithStringArgument,
        eventHandler, ...rest }: Props = $props();
</script>

Generic components

<script> of generics attribute accepts the same syntax as function generics:

<script lang="ts" generics="Item extends { text: string }">
  interface Props { items: Item[]; select(item: Item): void; }
  let { items, select }: Props = $props();
</script>
{#each items as item}
  <button onclick={() => select(item)}>{item.text}</button>
{/each}

Wrapper components (forwarding native attributes)

<script lang="ts">
  import type { HTMLButtonAttributes } from 'svelte/elements';
  let { children, ...rest }: HTMLButtonAttributes = $props();
</script>
<button {...rest}>{@render children?.()}</button>

For elements without a dedicated interface, use the SvelteHTMLElements['div'] form.Component / ComponentProps<T> is used to constrain dynamic components and extract the props of existing components.

In the class $state

class Counter {
  count = $state() as number; // 无初值类型会扩为 undefined
  constructor(initial: number) { this.count = initial; }
  increment() { this.count += 1; }
}

Augment svelte/elements

/// file: additional-svelte-typings.d.ts
declare module 'svelte/elements' {
  export interface SvelteHTMLElements { 'custom-button': HTMLButtonAttributes; }
  export interface HTMLAttributes<T> { globalattribute?: string; }
}
export {}; // 必须——否则会覆盖原模块

Critical: Custom Elements(Web Components)

Basic usage

<svelte:options customElement="my-element" />
<script>
  let { name = 'world' } = $props();
</script>
<h1>Hello {name}!</h1>
<slot />

Importing automatically customElements.define() registers. Consumers can read and write DOM properties:

const el = document.querySelector('my-element');
el.name = 'everybody'; // 触发 shadow DOM 更新

Props must be explicitly declared (you cannot just write let props = $props()), otherwise Svelte does not know which keys to expose.

customElement options

<svelte:options customElement={{
  tag: 'my-counter',
  shadow: 'open',
  props: { count: { type: 'Number', reflect: true, attribute: 'start' } },
  extend: (Ctor) => class extends Ctor { /* ... */ }
}} />
Field Value Default Purpose
tag string — Tag name; automatically registers when set
shadow 'none' | 'open' | 'closed' | ShadowRootInit 'open' Shadow root configuration
props.<name>.attribute string Lowercase prop name Custom attribute name
props.<name>.reflect boolean false Mirror the prop value to an HTML attribute
props.<name>.type 'String' | 'Boolean' | 'Number' | 'Array' | 'Object' 'String' Attribute ↔ prop type conversion
extend (Ctor) => class — Wrap the generated class (can inject lifecycle / ElementInternals)

$host() Rune + lifecycle

<svelte:options customElement="tooltip" />
<script>
  import { $host } from 'svelte';
  let { text = 'hi' } = $props();
  function focus() { $host().focus(); }
</script>
<button onclick={focus}>{text}</button>
  • Internal components are created at connectedCallback after thenext tick ; properties set before insertion are buffered and applied.
  • Shadow DOM updates are batched to the next tick; DOM moves do not trigger unnecessary unmounts.
  • disconnectedCallback destroyed at the next tick after. Component methods are available only after mount; to expose them earlier, declare them in the extend class.

Important considerations

  • StylesEncapsulationrather than "scoping"—global CSS cannot penetrate the shadow root.
  • CSS is inlined as a JS string rather than a separate .css file.
  • Notsuitable for SSR (shadow DOM is not visible before JS loads).
  • Slot contenteagerly rendered(unlike Svelte's default lazy rendering),<slot> does not copy content in {#each} .
  • let: directive on custom elementsinvalid。Svelte context cannot cross custom element boundaries.
  • Do not name on* attribute (interpreted as an event listener).

Critical: Browser Support

Svelte 5 targets Baseline 2020。

Browser Minimum version
Chrome / Edge 87
Firefox 83
Safari 14
Opera 73
Opera (Android) 62
Samsung Internet 14.0
Android WebView 87
Internet Explorer Not supported

Exceptions

Feature Chrome/Edge Firefox Safari
$state.snapshot 98 94 15.4
bind:devicePixelContentBoxSize — 93 Not supported
flip(from svelte/animate) — 126 —

SSR browser API protection

<script>
  import { onMount } from 'svelte';
  onMount(() => {
    window.addEventListener('resize', handleResize);
    return () => window.removeEventListener('resize', handleResize);
  });
</script>

Prefer <svelte:window> / <svelte:document>; Svelte automatically handles SSR and unmounting.


Critical: Best Practices

$state — declare only when needed

Only declare values that trigger updates ($effect / $derived / template) as $state. For large values that are only reassigned (API responses), use $state.raw to avoid Proxy overhead:

let user = $state.raw<User | null>(null);

$derived > $effect for pure computation

let square = $derived(num * num); // 好
// let square; $effect(() => { square = num * num; }); // 差

For complex logic use $derived.by(() => { ... }). Derived can be written; object/array results are not deeply proxied.

$effect is an escape hatch

Preferred tools: syncing with external libraries → {@attach ...}; responding to user input → handlers; debug logging → $inspect / $inspect.trace; observing external sources → createSubscriber. Never if (browser) { ... } wrap effects — effects do not run on the server anyway.

Treat props as mutable, use attribute syntax for events

let { type } = $props();
let color = $derived(type === 'danger' ? 'red' : 'green');

<svelte:window> / <svelte:document> for global listening; do not onMount manual binding. Snippets replace Slots,{#each ... (item.id)} use stable keys (index cannot be used).style:-- pass JS variables through to CSS custom properties; use CSS variables (instead of :global) to pierce child component styles; use createContext instead of module-level shared state (provides type safety + prevents leaks between SSR users).

Avoid Legacy in new code

on:click → onclick;export let → $props();$: → $derived / $effect;<slot> → snippet;use:action → {@attach ...}。


Critical: Svelte 4 Migration Guide

Minimum version

Dependency Required
Node 16+
SvelteKit 1.20.4+ (if used)
vite-plugin-svelte 2.4.1+
TypeScript 5+

Core migration comparison

Svelte 4 Svelte 5
let x = 0(top-level reactivity) let x = $state(0)
$: x = a + b let x = $derived(a + b)
$: { ... } $effect(() => { ... })
export let prop let { prop } = $props()
<slot /> {@render children()}
createEventDispatcher callback props
on:click={fn} onclick={fn}

Automated migration tools

npx svelte-migrate@latest svelte-5 my-project

Important behavior changes

  • Transitions default to local(outer control flow does not affect child transitions).
  • Default slot bindingswill notenter named slots.
  • Preprocessor order: markup → script → style (each executed in declaration order).
  • createEventDispatcher checks optional / required / no-parameter types in strict mode (later replaced by callback props).
  • Action / ActionReturn Default parameter type is undefined; declare explicitly when parameters are needed.
  • SvelteComponentTyped is deprecated, use SvelteComponent。
  • CJS output and svelte/register were removed.
  • Removed svelte.JSX, use svelteHTML / svelte/elements。
  • Bundlers must specify the browser condition (SvelteKit / Vite automatically; Rollup use @rollup/plugin-node-resolve to set browser: true; Webpack add in conditionNames add "browser")。

Critical: Svelte 5 Migration Guide (Concept Summary)

Runes system:$state / $derived / $effect replaces implicit reactivity; Snippets replace Slots; event attributes replace on:;$effect explicitly tracks dependencies;$state deeply proxies objects/arrays.

Removed Replacement
let top-level implicit reactivity $state
$: statement $derived / $effect
export let / $$props / $$restProps $props()
createEventDispatcher callback props / $bindable
Slots Snippets
on:event onevent
beforeUpdate / afterUpdate $effect.pre / $effect

New:$state.eager、$effect.pre、$effect.tracking、$state.snapshot、hydratable、fork()、{#key} transition trigger,<svelte:boundary>、createContext。


FAQ

Q: What is the core difference between Svelte and Vue/React? A: Svelte has no virtual DOM — it generates precise DOM update code at compile time, with zero framework runtime overhead.

Q: Can Svelte 5 and Svelte 4 coexist? A: Yes. Svelte 5 still supports Legacy Mode, and Svelte 4's $: and export let can continue to be used; you can migrate gradually within the same project.

Q: How do you upgrade to Svelte 5? A: ① Upgrade svelte + sveltekit / vite-plugin-svelte; ② Run npx svelte-migrate@latest svelte-5; ③ Gradually migrate to Runes Mode.

Q: Does Svelte support SSR / TypeScript? A: Both are natively supported. For SSR use render() or SvelteKit; for TS just use <script lang="ts">, and run in CI svelte-check。

Q: Does Svelte have a virtual DOM? A: No. The Svelte compiler generates precise DOM manipulation code (element.textContent = ...), rather than going through vDOM diffing.

Q: Which browsers support Svelte 5? A: Baseline 2020 (Chrome / Edge 87+, Firefox 83+, Safari 14+). IE is not supported; a few features require newer versions (see the table above).

Q: How is the styling of custom elements controlled? A: By default, shadow root mode 'open', global CSS will not penetrate. You can use shadow: 'none' to inherit global styles; use CSS variables to penetrate scoped styles.


Quick Fixes

Problem Solution
Svelte 4 code throws an error Confirm the component does not use $state(entering Runes Mode)
$: does not take effect Upgrade to Svelte 5; Legacy Mode is still supported
TypeScript throws an error Confirm <script lang="ts"> and TypeScript 5+
Bundle gets larger after migration Check the tree-shaking configuration
Properties starting with on are listened to Rename to avoid the on* prefix
Custom element SSR has no content Prerendering does not support shadow DOM; wait for JS to load
generic component type throws an error Check the generics string (same syntax as function generics)

Gotchas

  1. Svelte 5 automatically enters Runes Mode — as long as any Rune is used ($state , etc.), it is Runes Mode.
  2. Legacy Mode — It can be forced on via <svelte:options runes={false}> .
  3. on:click is deprecated — Legacy Mode is still available, but will warn.
  4. createEventDispatcher is deprecated — New code should use callback props.
  5. $bindable(default) — Only used as a fallback when the parent component does not pass the prop; if the parent component passes undefined , undefined will be preserved.
  6. bind:this Generic components — The instance type is Component<Props> | null, and an assertion is required.
  7. Custom element inline CSS — There is no .css file that can be linked by an external stylesheet.
  8. Server-side effect does not run — Do not use if (browser) to wrap the effect body.

Examples

Practical examples for Svelte 5 features.

  • Dynamic Components: svelte:element and {#key} blocks
  • Debug and HTML: @html, @debug, @const
  • TypeScript:
    • TypeScript Basics — Typed props, generics, event handlers, component refs
    • TypeScript Advanced — 14 patterns: preprocessors, generic constraints, wrapper components, Component type, $state in classes, svelte/elements augmentation, svelte-check, $bindable, form state
  • Custom Elements: Custom Elements — 12 patterns: customElement option, $host(), type coercion, reflection, extend, ElementInternals, slot semantics
  • Best Practices: Svelte 5 Best Practices — 14 patterns: $state.raw, $derived.by, keyed each blocks, CSS variables, createContext, async Svelte, legacy replacements
  • Migration: Svelte 3→4 Migration — 10 patterns: tag → customElement, stricter types, transition scope, preprocessor order, CJS removal
  • Browser Support: Browser Support — 7 patterns: feature detection, polyfills, bundler target, SSR guards, $state.snapshot fallback, Playwright pinning

References

In-depth reference documentation.

  • Dynamic Components: Dynamic Components Reference — svelte:element, {#key}, {@html}, @debug, @const
  • TypeScript:
  • Custom Elements: Custom Elements — customElement surface, $host() rune, lifecycle, caveats, ElementInternals
  • Best Practices: Best Practices — $state / $derived / $effect, props, events, snippets, each blocks, CSS variables, context, async
  • Browser Support: Browser Support — Minimum versions matrix, feature exceptions, polyfills, SSR safety, bundler config

Quick Links

Feature Reference
svelte:element Dynamic Components
{#key} Dynamic Components
{@html} / @debug / @const Dynamic Components
TS basics TypeScript Basics
TS advanced TypeScript Advanced
Generic components TypeScript Advanced
Wrapper components TypeScript Advanced
Component type TypeScript Advanced
DOM augmentation TypeScript Advanced
customElement options Custom Elements
$host() rune Custom Elements
Web Component lifecycle Custom Elements
$state / $derived / $effect patterns Best Practices
Async Svelte Best Practices
Browser matrix Browser Support
Feature exceptions Browser Support
Polyfills Browser Support