svelte-5-runes/SKILL.md
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.
npx skillsauth add szweibel/claude-skills svelte-5-runesInstall this skill globally with one command. Works with Claude Code, Cursor, and Windsurf.
3 of 9 scanners reported clean
Some scanners were skipped, did not run, or reported a non-clean status. Review each row below.
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.
Use this skill for any Svelte 5 project. Runes are the core reactivity primitives:
$state$derived$effect$props$bindable$inspectRunes are symbols prefixed with $ that control Svelte's compiler. They are:
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<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:
$state.raw() for non-reactive data<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:
<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:
DON'T use for:
$derived)$derived)<!-- 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>
<!-- 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>
<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>
<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>
<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>
❌ 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>
<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>
<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>
<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>
<!-- 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++} />
❌ Don't:
<script>
let { count } = $props();
</script>
<button onclick={() => count++}>
{count}
</button>
✅ Do:
<script>
let { count = $bindable() } = $props();
</script>
<button onclick={() => count++}>
{count}
</button>
<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>
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>
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>
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>
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>
Svelte 4:
<script>
export let name;
export let age = 18;
</script>
Svelte 5:
<script>
let { name, age = 18 } = $props();
</script>
Svelte 4:
<script>
export let value;
</script>
<input bind:value />
Svelte 5:
<script>
let { value = $bindable() } = $props();
</script>
<input bind:value />
For complete implementation details, read the reference files:
$effect.pre for before)$state creates deep reactive proxies for objects/arrays$state directly from .svelte.js if reassigned$bindabledevelopment
Comprehensive spreadsheet creation, editing, and analysis with support for formulas, formatting, data analysis, and visualization. When Claude needs to work with spreadsheets (.xlsx, .xlsm, .csv, .tsv, etc) for: (1) Creating new spreadsheets with formulas and formatting, (2) Reading or analyzing data, (3) Modify existing spreadsheets while preserving formulas, (4) Data analysis and visualization in spreadsheets, or (5) Recalculating formulas
development
Use OCLC WorldCat APIs to search for books and scholarly materials, retrieve bibliographic metadata, check library holdings worldwide, and get classification data. Use when working with ISBNs, DOIs, OCLC numbers, library catalogs, or institutional holdings.
tools
Toolkit for interacting with and testing local web applications using Playwright. Supports verifying frontend functionality, debugging UI behavior, capturing browser screenshots, and viewing browser logs.
tools
Guide for creating effective skills. This skill should be used when users want to create a new skill (or update an existing skill) that extends Claude's capabilities with specialized knowledge, workflows, or tool integrations.