Claude Code Plugins

Community-maintained marketplace

Feedback
0
0

Complete guide for Svelte 5 runes ($state, $derived, $effect, $props, $bindable). Use for any Svelte 5 project or when code contains $ prefixed runes. Essential for reactive state management, computed values, side effects, and component props. Covers migration from Svelte 4 reactive statements.

Install Skill

1Download skill
2Enable skills in Claude

Open claude.ai/settings/capabilities and find the "Skills" section

3Upload to Claude

Click "Upload skill" and select the downloaded ZIP file

Note: Please verify skill by going through its instructions before using it.

SKILL.md

name svelte-5-runes
description Complete guide for Svelte 5 runes ($state, $derived, $effect, $props, $bindable). Use for any Svelte 5 project or when code contains $ prefixed runes. Essential for reactive state management, computed values, side effects, and component props. Covers migration from Svelte 4 reactive statements.
license MIT
category development-tools

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