Skip to content

Instantly share code, notes, and snippets.

@diramazioni
Created May 10, 2025 10:16
Show Gist options
  • Save diramazioni/12ab0301eeb58379fae2056793974d6a to your computer and use it in GitHub Desktop.
Save diramazioni/12ab0301eeb58379fae2056793974d6a to your computer and use it in GitHub Desktop.
svelte5_full_context

Svelte 5 Runes Overview


$State



<script> let count = $state(0); // count is reactive </script>

<button onclick={() => count++}> clicks: {count}



<script> let todos = $state([ { done: false, text: 'Learn Svelte' }, { done: false, text: 'Build an app' } ]); function toggle(index) { todos[index].done = !todos[index].done; // Reactivity applies to individual items } </script>
    {#each todos as todo, index}
  • toggle(index)} /> {todo.text}
  • {/each}

<button onclick={() => todos.push({ done: false, text: 'New task' })}> Add Todo



<script> class Todo { done = $state(false); text = $state(''); constructor(text) { this.text = text; } reset = () => { this.text = ''; this.done = false; } } let todo = new Todo('Buy groceries'); </script>

{todo.text} - {todo.done ? 'Done' : 'Not done'}

todo.done = !todo.done}>Toggle todo.reset()}>Reset

<script> let person = $state.raw({ name: 'Alice', age: 30 }); function updatePerson() { // This won't work because `person` is raw (not reactive) person.age += 1; // Instead, we must reassign the whole object person = { ...person, age: person.age + 1 }; } </script>

{person.name} is {person.age} years old.

Increase Age

<script> let counter = $state({ count: 0 }); function logSnapshot() { console.log($state.snapshot(counter)); // Logs a plain object, not a Proxy } </script>

Count: {counter.count}

counter.count++}>Increment Log Snapshot

<script> function add(getA, getB) { return () => getA() + getB(); } let a = $state(1); let b = $state(2); let total = add(() => a, () => b); // Pass getters to maintain reactivity </script>

{a} + {b} = {total()}

a++}>Increment A b++}>Increment B
<script> let name = $state('Alice'); </script>

<input bind:value={ () => name, (v) => name = v.trimStart() } />

Hello, {name}!


$Derived



<script lang="ts"> let count = $state(0); let doubled = $derived(count * 2); // `doubled` automatically updates when `count` changes </script>

<button onclick={() => count++}> Increment

{count} doubled is {doubled}


  • $derived(count * 2) ensures doubled always reflects the latest count.
  • You cannot modify state inside $derived, e.g., count++ inside the expression.

<script lang="ts"> let numbers = $state([1, 2, 3]); // `total` is automatically recalculated when `numbers` change let total = $derived.by(() => { let sum = 0; for (const n of numbers) { sum += n; } return sum; }); </script>

<button onclick={() => numbers.push(numbers.length + 1)}> Add Number

{numbers.join(' + ')} = {total}


  • $derived.by(() => { ... }) is useful when the derived state requires loops or multiple steps.
  • The derived state (total) automatically updates when numbers change.

<script lang="ts"> let count = $state(0); let isEven = $derived(count % 2 === 0); </script>

<button onclick={() => count++}> Increment

{count} is {isEven ? 'Even' : 'Odd'}


  • $derived tracks all synchronously accessed state inside its expression.
  • If count changes, isEven will automatically update.

<script lang="ts"> let count = $state(0); let large = $derived(count > 10); </script>

<button onclick={() => count++}> Increment

Is count large? {large}


  • Svelte only updates when large changes (falsetrue or vice versa).
  • If count changes within the same threshold (e.g., 5 → 6), no update occurs.


$Effect



<script> let size = $state(50); let color = $state('#ff3e00'); let canvas; $effect(() => { const context = canvas.getContext('2d'); context.clearRect(0, 0, canvas.width, canvas.height); // This effect runs whenever `color` or `size` changes context.fillStyle = color; context.fillRect(0, 0, size, size); }); </script>

<script> let count = $state(0); let milliseconds = $state(1000); $effect(() => { const interval = setInterval(() => { count += 1; }, milliseconds); // Cleanup function runs before effect re-runs or on component destroy return () => { clearInterval(interval); }; }); </script>

{count}

<button onclick={() => (milliseconds *= 2)}>Slower <button onclick={() => (milliseconds /= 2)}>Faster



<script> let size = $state(50); let color = $state('#ff3e00'); let canvas; $effect(() => { const context = canvas.getContext('2d'); context.clearRect(0, 0, canvas.width, canvas.height); // This effect re-runs when `color` changes but not when `size` changes context.fillStyle = color; setTimeout(() => { context.fillRect(0, 0, size, size); }, 0); }); </script>

<script> import { tick } from 'svelte'; let div = $state(); let messages = $state([]); $effect.pre(() => { if (!div) return; // Tracks `messages.length` so the effect re-runs when messages change messages.length; // Auto-scroll logic before the DOM updates if (div.offsetHeight + div.scrollTop > div.scrollHeight - 20) { tick().then(() => { div.scrollTo(0, div.scrollHeight); }); } }); </script>
{#each messages as message}

{message}

{/each}

<button onclick={() => messages = [...messages, 'New message']}>Add Message



<script> let count = $state(0); let doubled = $derived(count * 2); // Correct approach // Incorrect approach (causes infinite loops): // $effect(() => { // doubled = count * 2; // }); </script>

{count} doubled is {doubled}

<button onclick={() => count += 1}>Increment




$Bindable



<script lang="ts"> let { value = $bindable(), ...props } = $props(); // `value` is bindable, allowing two-way data flow </script>

<input bind:value={value} {...props} />


<script lang="ts"> import FancyInput from './FancyInput.svelte'; let message = $state('hello'); // Using `$state` to create a reactive state variable </script>

{message}



<script lang="ts"> let { value = $bindable('fallback'), ...props } = $props(); // Default value set to 'fallback' </script>

<input bind:value={value} {...props} />


<script lang="ts"> import FancyInput from './FancyInput.svelte'; </script>



<script lang="ts"> let { value = $bindable('default text'), disabled = $bindable(false), ...props } = $props(); </script>

<input bind:value={value} bind:disabled={disabled} {...props} />


<script lang="ts"> import FancyInput from './FancyInput.svelte'; let message = $state('Editable text'); let isDisabled = $state(false); </script> isDisabled = !isDisabled}> Toggle Disabled
  • The input's value and disabled states can both be bound to the parent.
  • The button toggles the disabled state dynamically.
  • Useful for forms and UI elements requiring controlled states.

$Props



<script lang="ts"> import MyComponent from './MyComponent.svelte'; </script>
<script lang="ts"> let props = $props(); </script>

This component is {props.adjective}



<script lang="ts"> let { adjective } = $props(); </script>

This component is {adjective}



<script lang="ts"> let { adjective = 'happy' } = $props(); </script>

This component is {adjective}



<script lang="ts"> let { super: trouper = 'lights are gonna find me' } = $props(); </script>

Song lyric: {trouper}



<script lang="ts"> let { a, b, c, ...others } = $props(); </script>

a: {a}, b: {b}, c: {c}

Other props: {JSON.stringify(others)}



<script lang="ts"> import Child from './Child.svelte'; let count = $state(0); </script>

<button onclick={() => count += 1}> clicks (parent): {count}

<Child {count} />


<script lang="ts"> let { count } = $props(); </script>

<button onclick={() => count += 1}> clicks (child): {count}



<script lang="ts"> import Child from './Child.svelte'; </script>

<Child object={{ count: 0 }} />


<script lang="ts"> let { object } = $props(); </script>

<button onclick={() => object.count += 1}> clicks: {object.count}



<script lang="ts"> import Child from './Child.svelte'; let object = $state({ count: 0 }); </script>

<Child {object} />


<script lang="ts"> let { object } = $props(); </script>

<button onclick={() => object.count += 1}> clicks: {object.count}



<script lang="ts"> const uid = $props.id(); </script> First Name:
<label for="{uid}-lastname">Last Name: </label>
<input id="{uid}-lastname" type="text" />


$Host



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

<script lang="ts"> function dispatch(type) { $host().dispatchEvent(new CustomEvent(type)); // $host() gives access to the custom element itself. // Dispatches 'increment' or 'decrement' events. } </script>

<button onclick={() => dispatch('decrement')}>decrement <button onclick={() => dispatch('increment')}>increment


<script lang="ts"> import './Stepper.svelte'; let count = $state(0); // State variable to track count </script>

<my-stepper ondecrement={() => count -= 1} onincrement={() => count += 1}

count: {count}


  • $host() only works in components compiled as custom elements (<svelte:options customElement="..."/>).
  • This approach allows dispatching custom events from a shadow DOM encapsulated component.
  • Ensure the parent component listens to the dispatched events (ondecrement, onincrement).

$Inspect



<script> let count = $state(0); let message = $state('hello'); $inspect(count, message); // Logs `count` and `message` whenever they change </script>

<button onclick={() => count++}>Increment




<script> let count = $state(0); $inspect(count).with((type, count) => { if (type === 'update') { console.log(`Count updated to: ${count}`); } }); </script>

<button onclick={() => count++}>Increment




<script> let message = $state('Hello'); $inspect(message).with(console.trace); </script>


<script> import { doSomeWork } from './utils'; $effect(() => { $inspect.trace(); // Logs which state changes caused this effect to run doSomeWork(); }); </script>

Svelte 5 Event Handling

Svelte 5 replaces on: directives with direct property bindings for event handlers.


<script>
	let count = $state(0);

	function onclick() {
		count++;
	}
</script>

<button {onclick}>
	clicks: {count}
</button>

Key changes:

  • Removed on:click, replacing it with {onclick} as a property.
  • Uses a named function (onclick) instead of inline arrow functions.

In Svelte 5, components no longer use createEventDispatcher(). Instead, they accept callback props.


<script lang="ts">
	let { inflate, deflate } = $props(); // Props passed from parent
	let power = $state(5);
</script>

<button onclick={() => inflate(power)}>inflate</button>
<button onclick={() => deflate(power)}>deflate</button>
<button onclick={() => power--}>-</button>
Pump power: {power}
<button onclick={() => power++}>+</button>

<script lang="ts">
	import Pump from './Pump.svelte';

	let size = $state(15);
	let burst = $state(false);

	function inflateHandler(power) {
		size += power;
		if (size > 75) burst = true;
	}

	function deflateHandler(power) {
		if (size > 0) size -= power;
	}

	function reset() {
		size = 15;
		burst = false;
	}
</script>

<Pump inflate={inflateHandler} deflate={deflateHandler} />

{#if burst}
	<button onclick={reset}>new balloon</button>
	<span class="boom">💥</span>
{:else}
	<span class="balloon" style="scale: {0.01 * size}">
		🎈
	</span>
{/if}

Key changes:

  • Removed createEventDispatcher(), instead using callback props (inflate, deflate).
  • Events are now just function calls inside Pump.svelte.
  • Parent (App.svelte) passes handlers like inflateHandler to Pump.

To forward an event from an element to a component, use callback props instead of on:.


<script>
	let { onclick } = $props(); // Accepts an `onclick` function from parent
</script>

<button {onclick}>
	click me
</button>

Key changes:

  • No need to manually forward events like on:click{onclick} does the job.

Instead of manually listing every event, just spread props.


<script>
	let props = $props();
</script>

<button {...props}>
	click me
</button>

Key changes:

  • {...props} automatically applies all attributes, including event handlers like onclick.

Instead of duplicate event bindings, combine handlers manually.


<script>
	function one(event) {
		console.log('First handler', event);
	}

	function two(event) {
		console.log('Second handler', event);
	}
</script>

<button
	onclick={(e) => {
		one(e);
		two(e);
	}}
>
	...
</button>

Key changes:

  • Instead of multiple on:click={one} on:click={two}, handlers are manually merged.

Ensure that when spreading props, local event handlers still execute.


<script>
	let props = $props();

	function doStuff(event) {
		console.log('Handled locally', event);
	}
</script>

<button
	{...props}
	onclick={(e) => {
		doStuff(e);
		props.onclick?.(e);
	}}
>
	...
</button>

Key changes:

  • The local onclick executes before the props.onclick, preventing overwrites.

Modifiers like once and preventDefault should now be implemented manually.


<script>
	function once(fn) {
		return function (event) {
			if (fn) fn.call(this, event);
			fn = null; // Ensures it only runs once
		};
	}

	function preventDefault(fn) {
		return function (event) {
			event.preventDefault();
			fn.call(this, event);
		};
	}

	function handler(event) {
		console.log('Button clicked');
	}
</script>

<button onclick={once(preventDefault(handler))}>
	...
</button>

Key changes:

  • Used higher-order functions to replace on:click|once|preventDefault={handler}.


// svelte/events - on function // on attaches an event handler and returns a cleanup function. // It ensures correct handler order compared to addEventListener.

import { on } from 'svelte/events';

// Attach an event to window function on( window: Window, type: Type, handler: (this: Window, event: WindowEventMap[Type]) => any, options?: AddEventListenerOptions ): () => void;

// Attach an event to document function on( document: Document, type: Type, handler: (this: Document, event: DocumentEventMap[Type]) => any, options?: AddEventListenerOptions ): () => void;

// Attach an event to an HTMLElement function on<Element extends HTMLElement, Type extends keyof HTMLElementEventMap>( element: Element, type: Type, handler: (this: Element, event: HTMLElementEventMap[Type]) => any, options?: AddEventListenerOptions ): () => void;

// Attach an event to a MediaQueryList function on<Element extends MediaQueryList, Type extends keyof MediaQueryListEventMap>( element: Element, type: Type, handler: (this: Element, event: MediaQueryListEventMap[Type]) => any, options?: AddEventListenerOptions ): () => void;

// Generic event attachment function on( element: EventTarget, type: string, handler: EventListener, options?: AddEventListenerOptions ): () => void;



Use on:eventname={handler} to attach handlers (e.g., onclick={() => ...}).

  • Handlers can mutate $state directly; UI updates automatically.
  • Shorthand {eventname} works if the handler is a variable or function.
  • Events like click, input, etc., are delegated; assume bubbling unless stopped.
  • Events fire after bindings (e.g., oninput after bind:value).
  • Case-sensitive: onclickonClick (custom events may use uppercase).
<script> let count = $state(0); // Reactive state let text = $state(''); // Input value const logClick = () => console.log('Clicked!'); // Named handler // $effect to react to state changes $effect(() => console.log(`Count is now ${count}`)); </script>

<button onclick={() => count++}> Increment: {count}

<button {logClick}> Log Click

<input oninput={(e) => text = e.target.value} bind:value={text} placeholder="Type here" />

You typed: {text}

<input onkeydown={(e) => e.key === 'Enter' && count++} placeholder="Press Enter to increment" />

Note: For other events (e.g., onmouseover, onfocus), follow the same pattern: on:eventname={() => ...} or {eventname}. Adjust the handler logic based on the event’s purpose.

Svelte 5 Snippets


<script> let images = [ { src: "image1.jpg", caption: "Image 1", href: "https://example.com" }, { src: "image2.jpg", caption: "Image 2" } ]; </script>

{#snippet figure(image)}

{image.caption}

{image.caption} {/snippet}

{#each images as image} {#if image.href} {@render figure(image)} {:else} {@render figure(image)} {/if} {/each}



{#snippet x()} {#snippet y()}

Inside y

{/snippet}
	<!-- Rendering `y` inside `x` is allowed -->
	{@render y()}
{/snippet}

<!-- This will cause an error because `y` is out of scope -->
{@render y()}

{@render x()}



{#snippet blastoff()} 🚀 {/snippet}

{#snippet countdown(n)} {#if n > 0} {n}... {@render countdown(n - 1)} {:else} {@render blastoff()} {/if} {/snippet}

{@render countdown(5)}



<script> let { data, header, row } = $props(); </script> {@render header()} {#each data as item} {@render row(item)} {/each}

<script> import Table from "./Table.svelte"; const fruits = [ { name: "Apples", qty: 5, price: 2 }, { name: "Bananas", qty: 10, price: 1 }, { name: "Cherries", qty: 20, price: 0.5 } ]; </script>

{#snippet header()} Fruit Qty Price Total {/snippet}

{#snippet row(d)} {d.name} {d.qty} {d.price} {d.qty * d.price} {/snippet}



<script> let { children } = $props(); </script>

{@render children()}


Click Me



<script> let { children } = $props(); </script>

{#if children} {@render children()} {:else}

Fallback content

{/if}


{#snippet children()}

Custom content

{/snippet}

<script module> export { add }; </script>

{#snippet add(a, b)} {a} + {b} = {a + b} {/snippet}


<script> import { add } from "./math.svelte"; </script>

{@render add(2, 3)}



<script> let { header, main, footer } = $props(); </script> {@render header?.()} {@render main?.()} {@render footer?.()}
<script> import Layout from "./Layout.svelte"; </script> {#snippet header()}

My Website

{/snippet}
{#snippet main()}
	<p>Welcome to my website!</p>
{/snippet}

{#snippet footer()}
	<p>© 2023 My Website</p>
{/snippet}


<script> let { items, item, empty } = $props(); </script>

{#if items.length}

    {#each items as entry}
  • {@render item(entry)}
  • {/each}
{:else} {@render empty?.()} {/if}


<script> import List from "./List.svelte"; const fruits = ['Apple', 'Banana', 'Cherry']; </script> {#snippet item(fruit)} {fruit} {/snippet}
{#snippet empty()}
	<span>No fruits available</span>
{/snippet}

Svelte 5 + TypeScript


<script lang="ts"> let name: string = 'world'; function greet(name: string) { alert(`Hello, ${name}!`); } </script>

<button onclick={() => greet(name)}>Greet


Notes:

  • Adding lang="ts" enables TypeScript.
  • Type annotations (string) help with static checking.
  • No runtime overhead since TypeScript removes type annotations at compile time.


<script lang="ts"> interface Props { name: string; } let { name }: Props = $props(); </script>

Hello, {name}!


<script lang="ts"> import Greeting from './Greeting.svelte'; </script>

Notes:

  • Props interface ensures name is always a string.
  • $props() extracts the component’s props with proper typing.


<script lang="ts" generics="Item"> interface Props { items: Item[]; select: (item: Item) => void; } let { items, select }: Props = $props(); </script>

{#each items as item} <button onclick={() => select(item)}>{item} {/each}


<script lang="ts"> import List from './List.svelte'; const names = ['Alice', 'Bob', 'Charlie']; function handleSelect(name: string) { console.log('Selected:', name); } </script>

Notes:

  • generics="Item" allows the component to accept any item type.
  • Ensures items and select function operate on the same type.


<script lang="ts"> let count: number = $state(0); function increment() { count += 1; } </script>

Count: {count}


Notes:

  • $state(0) initializes a reactive variable with a type.
  • Without an initial value, TypeScript infers number | undefined.


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

<button {...rest}>{@render children?.()}


<script lang="ts"> import Button from './Button.svelte'; </script>

<Button onclick={() => alert('Clicked!')}>Click Me


Notes:

  • HTMLButtonAttributes ensures Button.svelte supports all standard button attributes.
  • ...rest spreads remaining props onto the <button>.


declare namespace svelteHTML { interface IntrinsicElements { 'custom-element': { customProp: string; 'on:customEvent': (e: CustomEvent) => void }; } }


<script lang="ts"> function handleEvent(e: CustomEvent) { console.log('Custom event:', e.detail); } </script>


Notes:

  • Extending IntrinsicElements allows TypeScript to recognize <custom-element> and its props.
  • Avoids TypeScript errors when using experimental or third-party web components.

Svelte 4 → 5 Migration Summary

Concise Summary (Svelte 4 → 5)

  • New Runes API
    • Reactive variables use let count = $state(0);
    • Derived values use const double = $derived(count * 2);
    • Side effects use
    $effect(() => {
      if (count > 5) alert('Count is too high!');
    });
  • Declaring Props
    • All props come from let {...} = $props();
    • Example:
    <script>
      let { optional = 'unset', required } = $props();
    </script>
  • Renaming or forwarding props:
    <script>
      let { class: klass, ...rest } = $props();
    </script>
    <button class={klass} {...rest}>click me</button>
Below is a concise, mostly bullet-pointed summary of the changes, focusing on code snippets:

Event Changes

  • Svelte 4 used on: directives.
  • Svelte 5: Use regular properties like onclick instead (no :).

Example:

<script>
  let count = $state(0);
</script>

<button onclick={() => count++}>
  clicks: {count}
</button>

Shorthand Example:

<script>
  let count = $state(0);

  function onclick() {
    count++;
  }
</script>

<button {onclick}>
  clicks: {count}
</button>

Component Events

  • Svelte 4: createEventDispatcher for emitting events.
  • Svelte 5: Use callback props instead.

Example (App.svelte):

<script lang="ts">
  import Pump from './Pump.svelte';

  let size = $state(15);
  let burst = $state(false);

  function reset() {
    size = 15;
    burst = false;
  }
</script>

<Pump
  inflate={(power) => {
    size += power;
    if (size > 75) burst = true;
  }}
  deflate={(power) => {
    if (size > 0) size -= power;
  }}
/>

{#if burst}
  <button onclick={reset}>new balloon</button>
  <span class="boom">💥</span>
{:else}
  <span class="balloon" style="scale: {0.01 * size}">
    🎈
  </span>
{/if}

Example (Pump.svelte):

<script lang="ts">
  let { inflate, deflate } = $props();
  let power = $state(5);
</script>

<button onclick={() => inflate(power)}>inflate</button>
<button onclick={() => deflate(power)}>deflate</button>

<button onclick={() => power--}>-</button>
Pump power: {power}
<button onclick={() => power++}>+</button>

Bubbling Events

  • No more <button on:click> to forward.
  • Accept a callback prop like onclick:
<script>
  let { onclick } = $props();
</script>

<button {onclick}>
  click me
</button>
  • Can “spread” handlers with other props:
<script>
  let props = $props();
</script>

<button {...props}>
  click me
</button>

Event Modifiers

  • Svelte 4 example: <button on:click|once|preventDefault={handler}>...
  • Svelte 5: No built-in modifiers. Handle event.preventDefault() in the function or wrap manually.

Example Wrappers:

<script>
  function once(fn) {
    return function (event) {
      if (fn) fn.call(this, event);
      fn = null;
    };
  }

  function preventDefault(fn) {
    return function (event) {
      event.preventDefault();
      fn.call(this, event);
    };
  }
</script>

<button onclick={once(preventDefault(handler))}>...</button>
  • For capture, do: onclickcapture={...}
  • For passive or nonpassive, use an action to attach the event.

Multiple Event Handlers

  • Svelte 4 allowed <button on:click={one} on:click={two}>.
  • Svelte 5: Only one onclick property. Combine them:
<button
  onclick={(e) => {
    one(e);
    two(e);
  }}
>
  ...
</button>
  • When spreading props, place local handlers afterward to avoid overwriting:
<button
  {...props}
  onclick={(e) => {
    doStuff(e);
    props.onclick?.(e);
  }}
>
  ...
</button>


Svelte 5 Changes - Condensed Bullet Points

Component Changes

  • No More Class Components → Components are now functions.
  • Instantiation → Use mount or hydrate instead of new Component().
  • Mount vs. Hydratehydrate picks up server-rendered HTML, otherwise identical to mount.
  • No $on, $set, $destroy
    • $on → Use events property instead (though callbacks are preferred).
    • $set → Use $state for reactivity.
    • $destroy → Use unmount.

Code Examples

import { mount } from 'svelte';
import App from './App.svelte';

const app = mount(App, { 
  target: document.getElementById("app"),
  events: { event: callback } // Replacement for $on
});

const props = $state({ foo: 'bar' }); // Replacement for $set
props.foo = 'baz';

import { unmount } from 'svelte';
unmount(app); // Replacement for $destroy

Legacy Compatibility

  • Use createClassComponent from svelte/legacy if needed.
  • compatibility.componentApi option enables auto-backwards compatibility.
export default {
  compilerOptions: {
    compatibility: {
      componentApi: 4
    }
  }
};

Asynchronous Behavior

  • mount and hydrate are not synchronous.
  • Use flushSync() if onMount must be guaranteed to run.

Server-Side Rendering (SSR)

  • No render() method in components.
  • Instead, use render() from svelte/server.
import { render } from 'svelte/server';
import App from './App.svelte';

const { html, head } = render(App, { props: { message: 'hello' }});
  • CSS not included by default → Use css: 'injected' to include styles.

Typing Changes

  • SvelteComponent deprecated → Use Component type.
  • ComponentEvents & ComponentType deprecated.
import type { Component } from 'svelte';
export declare const MyComponent: Component<{ foo: string }>;

bind:this Changes

  • No longer returns $set, $on, or $destroy methods.
  • Now only returns instance exports & property accessors (if enabled).

Dynamic Components

  • <svelte:component> no longer needed.
  • Components update dynamically when reassigned.
<script>
  import A from './A.svelte';
  import B from './B.svelte';
  let Thing = $state();
</script>

<select bind:value={Thing}>
  <option value={A}>A</option>
  <option value={B}>B</option>
</select>

<!-- Both are now equivalent -->
<Thing />
<svelte:component this={Thing} />

Other Changes

  • Dot Notation (<foo.bar>) now treated as a component, not an HTML tag.
  • Whitespace Handling Simplified → Predictable behavior.
  • Reserved children Prop → Cannot use children as a prop name; it's reserved.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment