install
source · Clone the upstream repo
git clone https://github.com/Intense-Visions/harness-engineering
Claude Code · Install into ~/.claude/skills/
T=$(mktemp -d) && git clone --depth=1 https://github.com/Intense-Visions/harness-engineering "$T" && mkdir -p ~/.claude/skills && cp -r "$T/agents/skills/claude-code/svelte-runes-pattern" ~/.claude/skills/intense-visions-harness-engineering-svelte-runes-pattern-b63e97 && rm -rf "$T"
manifest:
agents/skills/claude-code/svelte-runes-pattern/SKILL.mdsource content
Svelte Runes Pattern
Declare reactive state, derived values, and side effects in Svelte 5 using the runes API ($state, $derived, $effect, $props, $bindable)
When to Use
- You are writing Svelte 5 components and need reactive local state
- You are migrating a Svelte 4 component from
declarations andlet x
reactive statements$: - You need to declare component props with type safety and optional defaults
- You want to create two-way bindable props for form inputs or controlled components
Instructions
$state — reactive local state:
- Declare reactive variables with
. Unlike Svelte 4's magic$state
,let
is explicit and works anywhere (not just$state
files):.svelte
<script lang="ts"> let count = $state(0) let items = $state<string[]>([]) let user = $state<User | null>(null) </script> <button onclick={() => count++}>{count}</button>
- Objects and arrays declared with
are deeply reactive — mutations are tracked automatically:$state
<script lang="ts"> let todos = $state([ { id: 1, text: 'Buy milk', done: false } ]) function toggle(id: number) { const todo = todos.find(t => t.id === id) if (todo) todo.done = !todo.done // mutation tracked } </script>
- Use
for large non-reactive objects that should only update on full reassignment:$state.raw
let data = $state.raw<HeavyObject>(initialData); // later: data = newData; // triggers update; internal mutations do not
$derived — computed values:
- Replace
with$: computed = expr
:$derived
<script lang="ts"> let count = $state(0) let doubled = $derived(count * 2) let isEven = $derived(count % 2 === 0) </script>
- Use
for multi-line derived logic:$derived.by
let filteredItems = $derived.by(() => { return items.filter((item) => item.active).sort((a, b) => a.name.localeCompare(b.name)); });
$effect — side effects:
- Replace
and$: { sideEffect() }
withonMount
. It runs after DOM updates, re-runs when dependencies change, and cleans up on re-run or destroy:$effect
$effect(() => { const handler = (e: KeyboardEvent) => { if (e.key === 'Escape') closeModal(); }; window.addEventListener('keydown', handler); return () => window.removeEventListener('keydown', handler); // cleanup });
does NOT run on the server. For SSR-compatible initialization, use$effect
fromonMount
:svelte
import { onMount } from 'svelte'; onMount(() => { initMap(); });
$props — component props:
- Declare props with
— it replaces$props()
declarations:export let
<script lang="ts"> interface Props { name: string age?: number onSelect?: (value: string) => void } let { name, age = 18, onSelect }: Props = $props() </script>
- Spread remaining props onto an element using rest destructuring:
<script lang="ts"> let { class: className, ...rest }: { class?: string } & Record<string, unknown> = $props() </script> <div class={className} {...rest} />
$bindable — two-way bound props:
- Mark a prop as bindable to allow parent components to use
:bind:propName
<!-- Child: InputField.svelte --> <script lang="ts"> let { value = $bindable('') }: { value?: string } = $props() </script> <input bind:value /> <!-- Parent: --> <InputField bind:value={searchQuery} />
Details
Runes vs. Svelte 4 reactivity:
| Svelte 4 | Svelte 5 Rune |
|---|---|
| |
| |
| |
| |
| |
Runes work outside .svelte files:
This is the key architectural difference from Svelte 4.
$state, $derived, and $effect work in .svelte.ts and .svelte.js files — enabling reactive shared logic without stores:
// lib/useCounter.svelte.ts export function useCounter(initial = 0) { let count = $state(initial); const doubled = $derived(count * 2); return { get count() { return count; }, get doubled() { return doubled; }, increment: () => count++, }; }
Getter pattern for exposing $state:
To expose reactive state from a function, return getters (not values) so the reactive signal flows through:
// Do this: return { get count() { return count; }, }; // Not this (loses reactivity): return { count };
$effect dependency tracking:
$effect automatically tracks all $state and $derived values read during its execution. You do not declare dependencies manually. If you read a value inside a conditional branch, it only becomes a dependency when that branch is reached.
Source
https://svelte.dev/docs/svelte/what-are-runes
Process
- Read the instructions and examples in this document.
- Apply the patterns to your implementation, adapting to your specific context.
- Verify your implementation against the details and edge cases listed above.
Harness Integration
- Type: knowledge — this skill is a reference document, not a procedural workflow.
- No tools or state — consumed as context by other skills and agents.
Success Criteria
- The patterns described in this document are applied correctly in the implementation.
- Edge cases and anti-patterns listed in this document are avoided.