Skills svelte
install
source · Clone the upstream repo
git clone https://github.com/TerminalSkills/skills
Claude Code · Install into ~/.claude/skills/
T=$(mktemp -d) && git clone --depth=1 https://github.com/TerminalSkills/skills "$T" && mkdir -p ~/.claude/skills && cp -r "$T/skills/svelte" ~/.claude/skills/terminalskills-skills-svelte && rm -rf "$T"
manifest:
skills/svelte/SKILL.mdsource content
Svelte — Compile-Time Reactive UI Framework
Overview
Svelte shifts work from runtime to compile time, producing minimal vanilla JavaScript with no virtual DOM. Svelte 5 introduces runes — explicit reactivity primitives that replace
$: declarations and stores. SvelteKit 2 provides SSR, routing, and deployment adapters.
Instructions
Runes Reference
— reactive state (deep by default):$state
<script lang="ts"> let count = $state(0); let user = $state({ name: "Alice", age: 30 }); user.age++; // deep reactivity — triggers updates </script>
— shallow reactive (must reassign to trigger):$state.raw
<script> let items = $state.raw([1, 2, 3]); items = [...items, 4]; // must reassign </script>
— plain non-reactive copy:$state.snapshot
<script> let form = $state({ name: "", email: "" }); const data = $state.snapshot(form); // plain object for API calls </script>
and $derived
— computed values:$derived.by
<script> let price = $state(100); let quantity = $state(3); let total = $derived(price * quantity); let summary = $derived.by(() => { const t = items.reduce((sum, i) => sum + i.price, 0); return { total: t, count: items.length, avg: t / items.length }; }); </script>
— side effects with cleanup:$effect
<script> let query = $state(""); $effect(() => { if (!query) return; const controller = new AbortController(); fetch(`/api/search?q=${query}`, { signal: controller.signal }) .then(r => r.json()).then(data => { results = data; }); return () => controller.abort(); }); </script>
— component props:$props
<script> let { label, variant = "primary", onclick, class: className = "", ...rest } = $props(); </script> <button class="btn btn-{variant} {className}" {onclick} {...rest}>{label}</button>
— two-way binding:$bindable
<!-- Input.svelte --> <script> let { value = $bindable(""), placeholder = "" } = $props(); </script> <input bind:value {placeholder} />
— debug reactive values (dev only):$inspect
<script> $inspect(count, doubled); $inspect(count).with((type, value) => console.log(`[${type}]`, value)); </script>
Snippets (Replacing Slots)
Svelte 5 replaces slots with typed, reusable markup fragments:
<!-- List.svelte --> <script> let { items, row, children } = $props(); </script> <ul> {#each items as item} <li>{@render row(item)}</li> {/each} </ul> {@render children?.()}
<!-- Parent.svelte --> <List {items}> {#snippet row(item)} <strong>{item.name}</strong> — {item.description} {/snippet} </List>
Stores (Shared State)
// stores/cart.ts — Writable store (Svelte 4 style, still works) import { writable, derived } from "svelte/store"; export const cart = writable<CartItem[]>([]); export const cartTotal = derived(cart, ($cart) => $cart.reduce((sum, item) => sum + item.price * item.quantity, 0) );
Svelte 5 alternative —
$state in .svelte.ts modules:
// state/counter.svelte.ts let count = $state(0); let doubled = $derived(count * 2); export function getCounter() { return { get count() { return count; }, get doubled() { return doubled; }, increment() { count++; }, }; }
Transitions
<script> import { fade, fly, slide } from "svelte/transition"; import { flip } from "svelte/animate"; let items = $state(["Apple", "Banana"]); </script> {#each items as item (item)} <div animate:flip={{ duration: 300 }} transition:fade>{item}</div> {/each}
Migration from Svelte 4
| Svelte 4 | Svelte 5 |
|---|---|
| |
| |
| |
| |
| |
| |
| Svelte stores | in files |
SvelteKit 2
<!-- +page.svelte --> <script> let { data } = $props(); // from +page.ts load function </script> <h1>{data.title}</h1>
npx sv create my-app # SvelteKit project npm create vite@latest -- --template svelte-ts # standalone Svelte
Examples
Example 1: Shopping cart with stores
<!-- CartSummary.svelte --> <script lang="ts"> import { cart, cartTotal, removeFromCart } from "$lib/stores/cart"; </script> <div class="cart"> {#each $cart as item (item.id)} <div>{item.name} x {item.quantity} — ${(item.price * item.quantity).toFixed(2)} <button onclick={() => removeFromCart(item.id)}>Remove</button> </div> {/each} <p>Total: ${$cartTotal.toFixed(2)}</p> </div>
Example 2: Async data fetching with $effect
<script> let userId = $state(1); let user = $state(null); let loading = $state(false); $effect(() => { loading = true; fetch(`/api/users/${userId}`) .then(r => r.json()) .then(data => { user = data; loading = false; }); }); </script> {#if loading}<p>Loading...</p> {:else if user}<p>{user.name}</p>{/if}
Guidelines
- Runes for state — Use
,$state()
,$derived()
in Svelte 5; cleaner than Svelte 4's$effect()
syntax$: - Scoped styles — CSS in
is component-scoped; no CSS modules needed<style> - Stores vs runes — Use
in$state
for new code; writable/derived stores still work.svelte.ts - Transitions built-in — Use
,transition:fade
; no animation library neededtransition:fly - Small bundles — Svelte compiles away; typical app ships 5-10KB vs 40KB+ for React
- SvelteKit for full-stack — SSR, routing, API endpoints, deployment adapters
- TypeScript — Use
for type-safe components<script lang="ts"> - Actions for DOM — Use
for reusable DOM behavior (click-outside, tooltip)use:action