Smithery Logo
MCPsSkillsDocsPricing
Login
Smithery Logo

Accelerating the Agent Economy

Resources

DocumentationPrivacy PolicySystem Status

Company

PricingAboutBlog

Connect

© 2026 Smithery. All rights reserved.

    szweibel

    svelte-5-runes

    szweibel/svelte-5-runes
    Coding
    1

    About

    SKILL.md

    Install

    Install via Skills CLI

    or add to your agent
    • Claude Code
      Claude Code
    • Codex
      Codex
    • OpenClaw
      OpenClaw
    • Cursor
      Cursor
    • Amp
      Amp
    • GitHub Copilot
      GitHub Copilot
    • Gemini CLI
      Gemini CLI
    • Kilo Code
      Kilo Code
    • Junie
      Junie
    • Replit
      Replit
    • Windsurf
      Windsurf
    • Cline
      Cline
    • Continue
      Continue
    • OpenCode
      OpenCode
    • OpenHands
      OpenHands
    • Roo Code
      Roo Code
    • Augment
      Augment
    • Goose
      Goose
    • Trae
      Trae
    • Zencoder
      Zencoder
    • Antigravity
      Antigravity
    ├─
    ├─
    └─

    About

    Complete guide for Svelte 5 runes ($state, $derived, $effect, $props, $bindable). Use for any Svelte 5 project or when code contains $ prefixed runes.

    SKILL.md

    Svelte 5 Runes

    Feature Status: Stable in Svelte 5.0+ (October 2024) Documentation Source: https://svelte.dev/docs/svelte/what-are-runes

    Complete guide to Svelte 5's reactivity system using runes.

    When to Use This Skill

    Use this skill for any Svelte 5 project. Runes are the core reactivity primitives:

    • Creating reactive state with $state
    • Computing derived values with $derived
    • Running side effects with $effect
    • Defining component props with $props
    • Creating bindable props with $bindable
    • Debugging with $inspect

    What Are Runes?

    Runes are symbols prefixed with $ that control Svelte's compiler. They are:

    • Built-in language keywords - No imports required
    • Not functions - Cannot be assigned to variables or passed around
    • Position-dependent - Only valid in specific contexts

    Available Runes:

    • $state - Declare reactive state
    • $derived - Create computed values
    • $effect - Manage side effects
    • $props - Define component properties
    • $bindable - Enable two-way binding on props
    • $inspect - Debug tool for state inspection
    • $host - Access custom element host (web components)
    • $props.id() - Generate consistent component-scoped IDs

    Quick Start Patterns

    Reactive State ($state)

    <script>
    	let count = $state(0);
    	let user = $state({ name: 'Ada', age: 28 });
    </script>
    
    <button onclick={() => count++}>
    	Clicks: {count}
    </button>
    
    <button onclick={() => user.age++}>
    	Happy birthday {user.name}! Age: {user.age}
    </button>
    

    Key features:

    • Deep reactivity by default (objects/arrays)
    • Mutations trigger UI updates
    • Use $state.raw() for non-reactive data
    • Works in class fields

    Computed Values ($derived)

    <script>
    	let count = $state(0);
    	let doubled = $derived(count * 2);
    
    	// Complex derivations
    	let status = $derived.by(() => {
    		if (count < 5) return 'low';
    		if (count < 10) return 'medium';
    		return 'high';
    	});
    </script>
    
    <p>{count} doubled is {doubled}</p>
    <p>Status: {status}</p>
    

    When to use:

    • Transforming state values
    • Computing aggregations
    • Filtering/mapping arrays
    • ANY value derived from other state

    Side Effects ($effect)

    <script>
    	let count = $state(0);
    	let canvas;
    
    	$effect(() => {
    		// Runs when count changes
    		console.log('Count changed:', count);
    
    		// Cleanup function (optional)
    		return () => {
    			console.log('Cleanup before re-run');
    		};
    	});
    
    	$effect(() => {
    		if (!canvas) return;
    		const ctx = canvas.getContext('2d');
    		ctx.clearRect(0, 0, 100, 100);
    		ctx.fillRect(0, 0, count, count);
    	});
    </script>
    
    <canvas bind:this={canvas} width="100" height="100"></canvas>
    

    Use for:

    • Canvas drawing
    • Third-party library integration
    • Analytics tracking
    • Network requests (with caution)

    DON'T use for:

    • Synchronizing state (use $derived)
    • Computing values (use $derived)

    Component Props ($props)

    <!-- Parent.svelte -->
    <script>
    	import Child from './Child.svelte';
    </script>
    
    <Child name="Ada" age={28} />
    
    <!-- Child.svelte -->
    <script>
    	// Destructure with defaults
    	let { name, age = 18 } = $props();
    
    	// Or get all props
    	let props = $props();
    </script>
    
    <p>{name} is {age} years old</p>
    

    TypeScript:

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

    Two-Way Binding ($bindable)

    <!-- TextInput.svelte -->
    <script>
    	let { value = $bindable(''), placeholder } = $props();
    </script>
    
    <input bind:value {placeholder} />
    
    <!-- App.svelte -->
    <script>
    	import TextInput from './TextInput.svelte';
    
    	let message = $state('');
    </script>
    
    <TextInput bind:value={message} placeholder="Type here" />
    <p>You typed: {message}</p>
    

    Common Workflows

    Form with Validation

    <script>
    	let email = $state('');
    	let password = $state('');
    
    	let isValidEmail = $derived(email.includes('@') && email.includes('.'));
    	let isValidPassword = $derived(password.length >= 8);
    	let canSubmit = $derived(isValidEmail && isValidPassword);
    
    	function handleSubmit() {
    		if (!canSubmit) return;
    		// Submit form
    	}
    </script>
    
    <form onsubmit={handleSubmit}>
    	<input bind:value={email} type="email" />
    	{#if email && !isValidEmail}
    		<p class="error">Invalid email</p>
    	{/if}
    
    	<input bind:value={password} type="password" />
    	{#if password && !isValidPassword}
    		<p class="error">Password must be 8+ characters</p>
    	{/if}
    
    	<button disabled={!canSubmit}>Submit</button>
    </form>
    

    Data Fetching with Loading States

    <script>
    	let userId = $state(1);
    	let userData = $state(null);
    	let loading = $state(false);
    	let error = $state(null);
    
    	$effect(() => {
    		// Tracks userId - refetches when it changes
    		loading = true;
    		error = null;
    
    		fetch(`/api/users/${userId}`)
    			.then(r => r.json())
    			.then(data => {
    				userData = data;
    				loading = false;
    			})
    			.catch(e => {
    				error = e.message;
    				loading = false;
    			});
    	});
    </script>
    
    {#if loading}
    	<p>Loading...</p>
    {:else if error}
    	<p class="error">{error}</p>
    {:else if userData}
    	<div>
    		<h2>{userData.name}</h2>
    		<p>{userData.email}</p>
    	</div>
    {/if}
    
    <button onclick={() => userId++}>Next User</button>
    

    Class-Based State

    <script>
    	class Counter {
    		count = $state(0);
    
    		increment = () => {
    			this.count++;
    		}
    
    		reset() {
    			this.count = 0;
    		}
    	}
    
    	let counter = new Counter();
    </script>
    
    <button onclick={counter.increment}>
    	Count: {counter.count}
    </button>
    <button onclick={() => counter.reset()}>Reset</button>
    

    Best Practices

    1. Use $derived, Not $effect for Computed Values

    ❌ Don't do this:

    <script>
    	let count = $state(0);
    	let doubled = $state(0);
    
    	$effect(() => {
    		doubled = count * 2;
    	});
    </script>
    

    ✅ Do this:

    <script>
    	let count = $state(0);
    	let doubled = $derived(count * 2);
    </script>
    

    2. Understand Deep Reactivity

    <script>
    	let items = $state([1, 2, 3]);
    
    	// All these trigger reactivity:
    	items.push(4);          // ✅ Works
    	items[0] = 10;          // ✅ Works
    	items = items.filter(x => x > 1); // ✅ Works
    
    	// Destructuring breaks reactivity:
    	let [first] = items;
    	items[0] = 99;          // first still has old value
    </script>
    

    3. Use $state.raw for Large Immutable Data

    <script>
    	// Don't need deep reactivity - performance win
    	let config = $state.raw({
    		apiUrl: 'https://api.example.com',
    		timeout: 5000,
    		// ... hundreds of properties
    	});
    
    	// Only reassign, don't mutate
    	config = { ...config, timeout: 10000 };
    </script>
    

    4. Effect Dependencies Are Automatic

    <script>
    	let a = $state(1);
    	let b = $state(2);
    	let c = $state(3);
    
    	$effect(() => {
    		// Only depends on `a` and `b`
    		console.log(a + b);
    
    		// `c` is not a dependency - won't rerun when c changes
    	});
    </script>
    

    5. Props Flow Down, Events Flow Up

    <!-- Child.svelte -->
    <script>
    	let { count, onIncrement } = $props();
    </script>
    
    <button onclick={onIncrement}>
    	Count: {count}
    </button>
    
    <!-- Parent.svelte -->
    <script>
    	import Child from './Child.svelte';
    
    	let count = $state(0);
    </script>
    
    <Child {count} onIncrement={() => count++} />
    

    6. Don't Mutate Props (Use $bindable)

    ❌ Don't:

    <script>
    	let { count } = $props();
    </script>
    
    <button onclick={() => count++}>
    	{count}
    </button>
    

    ✅ Do:

    <script>
    	let { count = $bindable() } = $props();
    </script>
    
    <button onclick={() => count++}>
    	{count}
    </button>
    

    7. Use $inspect for Debugging

    <script>
    	let count = $state(0);
    
    	$inspect(count); // Logs when count changes with stack trace
    
    	// Custom logging
    	$inspect(count).with((type, value) => {
    		if (type === 'update') {
    			console.log('Count updated to:', value);
    		}
    	});
    </script>
    

    Common Pitfalls

    Async State in Effects

    Issue: Async code doesn't track dependencies.

    <script>
    	let userId = $state(1);
    	let user = $state(null);
    
    	$effect(() => {
    		// userId IS tracked (read before await)
    		const id = userId;
    
    		fetch(`/api/users/${id}`)
    			.then(r => r.json())
    			.then(data => {
    				// This works, but any state read HERE
    				// is NOT tracked as a dependency
    				user = data;
    			});
    	});
    </script>
    

    Effect Cleanup

    Always cleanup subscriptions:

    <script>
    	let count = $state(0);
    
    	$effect(() => {
    		const interval = setInterval(() => {
    			count++;
    		}, 1000);
    
    		// Cleanup when effect re-runs or component unmounts
    		return () => clearInterval(interval);
    	});
    </script>
    

    Infinite Loops

    Don't read and write same state in effect:

    <script>
    	let count = $state(0);
    
    	// ❌ Infinite loop!
    	$effect(() => {
    		count = count + 1;
    	});
    
    	// ✅ Use derived instead
    	let incremented = $derived(count + 1);
    </script>
    

    Migration from Svelte 4

    Reactive Declarations

    Svelte 4:

    <script>
    	let count = 0;
    	$: doubled = count * 2;
    	$: console.log('Count changed:', count);
    </script>
    

    Svelte 5:

    <script>
    	let count = $state(0);
    	let doubled = $derived(count * 2);
    
    	$effect(() => {
    		console.log('Count changed:', count);
    	});
    </script>
    

    Component Props

    Svelte 4:

    <script>
    	export let name;
    	export let age = 18;
    </script>
    

    Svelte 5:

    <script>
    	let { name, age = 18 } = $props();
    </script>
    

    Two-Way Binding

    Svelte 4:

    <script>
    	export let value;
    </script>
    
    <input bind:value />
    

    Svelte 5:

    <script>
    	let { value = $bindable() } = $props();
    </script>
    
    <input bind:value />
    

    Detailed Documentation

    For complete implementation details, read the reference files:

    • references/quick-reference.md - All runes syntax at a glance
    • references/state.md - Complete $state documentation (deep reactivity, classes, raw state)
    • references/derived.md - $derived patterns and dependencies
    • references/effect.md - $effect lifecycle, cleanup, and when NOT to use
    • references/props.md - $props type safety and patterns
    • references/other-runes.md - $bindable, $inspect, $host usage

    Important Notes

    • Runes are compile-time features - no runtime imports
    • Effects run after DOM updates (use $effect.pre for before)
    • Dependencies are tracked synchronously - async code won't track
    • $state creates deep reactive proxies for objects/arrays
    • Can't export $state directly from .svelte.js if reassigned
    • Props should not be mutated unless marked with $bindable
    Repository
    szweibel/claude-skills
    Files