🎯

svelte-development

🎯Skill

from travisjneuman/.claude

VibeIndex|
What it does

Enables building reactive, type-safe Svelte 5 applications with runes, SvelteKit, and modern web development best practices.

πŸ“¦

Part of

travisjneuman/.claude(62 items)

svelte-development

Installation

npxRun with npx
npx sv migrate svelte-5
πŸ“– Extracted from docs: travisjneuman/.claude
3Installs
-
AddedFeb 4, 2026

Skill Details

SKILL.md

Svelte 5 development with runes ($state, $derived, $effect), SvelteKit full-stack framework, and modern reactive patterns. Use when building Svelte applications, implementing fine-grained reactivity, or working with SvelteKit routing and server functions.

Overview

# Svelte 5 Development

Comprehensive guide for building modern Svelte applications with runes and SvelteKit.

Stack Overview

| Tool | Purpose | Version |

| ---------- | -------------------- | ------- |

| Svelte 5 | Core framework | 5.0+ |

| SvelteKit | Full-stack framework | 2.0+ |

| TypeScript | Type safety | 5.0+ |

| Vite | Build tool | 5.0+ |

| Vitest | Testing | 1.0+ |

---

Svelte 5 Runes (Core Reactivity)

The Rune System

Runes are compile-time macros prefixed with $ that provide explicit, fine-grained reactivity.

| Rune | Purpose | Replaces |

| ----------- | --------------------- | --------------------------- |

| $state | Reactive state | let declarations |

| $derived | Computed values | $: reactive statements |

| $effect | Side effects | $: side effect statements |

| $props | Component props | export let |

| $bindable | Two-way binding props | export let with bind: |

Basic Component Structure

```svelte

{title}

Count: {localCount} (Doubled: {doubled})

```

State Patterns

```typescript

// Primitives

let count = $state(0);

let name = $state("");

// Objects (deeply reactive)

let user = $state({

name: "John",

email: "john@example.com",

preferences: { theme: "dark" },

});

// Arrays

let items = $state([]);

// Direct mutation works!

user.name = "Jane"; // Reactive

user.preferences.theme = "light"; // Reactive (deep)

items.push({ id: 1, name: "New" }); // Reactive

// Frozen state (shallow reactivity)

let frozenList = $state.frozen([1, 2, 3]);

```

Derived Values

```typescript

// Simple derived

let doubled = $derived(count * 2);

// Complex derived (use $derived.by for multi-line)

let stats = $derived.by(() => {

const total = items.reduce((sum, i) => sum + i.value, 0);

const average = items.length ? total / items.length : 0;

return { total, average, count: items.length };

});

// Derived from multiple sources

let summary = $derived(${user.name} has ${items.length} items);

```

Effects

```typescript

// Basic effect (runs when dependencies change)

$effect(() => {

console.log(Count is now: ${count});

});

// Effect with cleanup

$effect(() => {

const interval = setInterval(() => {

count++;

}, 1000);

// Cleanup function (returned)

return () => clearInterval(interval);

});

// Pre-effect (runs before DOM updates)

$effect.pre(() => {

console.log("About to update DOM");

});

// Root effect (doesn't track dependencies)

$effect.root(() => {

// Manual dependency management

});

```

---

Component Patterns

Props with Defaults and Spreading

```svelte

class="btn btn-{variant} btn-{size}"

{disabled}

{...restProps}

>

{@render children()}

```

Two-Way Binding with $bindable

```svelte

Hello, {name}!

```

Snippets (Replacing Slots)

```svelte

{title}

{@render children()}

{#if footer}

{@render footer()}

{/if}

Card content goes here

{#snippet footer()}

{/snippet}

```

Snippets with Parameters

```svelte

{#if items.length === 0}

{#if empty}

{@render empty()}

{:else}

No items

{/if}

{:else}

    {#each items as item, index}

  • {@render row(item, index)}
  • {/each}

{/if}

{#snippet row(user, i)}

{i + 1}. {user.name}

{/snippet}

{#snippet empty()}

No users found

{/snippet}

```

---

Shared State with .svelte.ts Files

Creating Shared State

```typescript

// lib/stores/counter.svelte.ts

export function createCounter(initial = 0) {

let count = $state(initial);

return {

get count() {

return count;

},

increment() {

count++;

},

decrement() {

count--;

},

reset() {

count = initial;

},

};

}

// Singleton instance

export const counter = createCounter();

```

Class-Based State

```typescript

// lib/stores/user.svelte.ts

export class UserStore {

user = $state(null);

loading = $state(false);

error = $state(null);

isLoggedIn = $derived(!!this.user);

async login(email: string, password: string) {

this.loading = true;

this.error = null;

try {

const response = await fetch("/api/auth/login", {

method: "POST",

body: JSON.stringify({ email, password }),

headers: { "Content-Type": "application/json" },

});

if (!response.ok) throw new Error("Login failed");

this.user = await response.json();

} catch (e) {

this.error = e instanceof Error ? e.message : "Unknown error";

throw e;

} finally {

this.loading = false;

}

}

logout() {

this.user = null;

}

}

export const userStore = new UserStore();

```

Using Shared State

```svelte

Count: {counter.count}

{#if userStore.isLoggedIn}

Welcome, {userStore.user?.name}

{:else}

{/if}

```

---

SvelteKit (Full-Stack)

Project Structure

```

sveltekit-app/

β”œβ”€β”€ src/

β”‚ β”œβ”€β”€ routes/ # File-based routing

β”‚ β”‚ β”œβ”€β”€ +page.svelte # /

β”‚ β”‚ β”œβ”€β”€ +page.server.ts # Server load function

β”‚ β”‚ β”œβ”€β”€ +layout.svelte # Root layout

β”‚ β”‚ β”œβ”€β”€ about/

β”‚ β”‚ β”‚ └── +page.svelte # /about

β”‚ β”‚ β”œβ”€β”€ users/

β”‚ β”‚ β”‚ β”œβ”€β”€ +page.svelte # /users

β”‚ β”‚ β”‚ └── [id]/

β”‚ β”‚ β”‚ β”œβ”€β”€ +page.svelte # /users/:id

β”‚ β”‚ β”‚ └── +page.server.ts

β”‚ β”‚ └── api/

β”‚ β”‚ └── users/

β”‚ β”‚ └── +server.ts # /api/users

β”‚ β”œβ”€β”€ lib/ # $lib alias

β”‚ β”‚ β”œβ”€β”€ components/

β”‚ β”‚ └── stores/

β”‚ └── app.html

β”œβ”€β”€ static/

└── svelte.config.js

```

Load Functions

```typescript

// routes/users/+page.server.ts

import type { PageServerLoad } from './$types'

import { error } from '@sveltejs/kit'

export const load: PageServerLoad = async ({ fetch, params }) => {

const response = await fetch('/api/users')

if (!response.ok) {

throw error(response.status, 'Failed to load users')

}

const users = await response.json()

return { users }

}

// routes/users/+page.svelte

Users

{#each data.users as user}

{user.name}

{/each}

```

Form Actions

```typescript

// routes/login/+page.server.ts

import type { Actions } from "./$types";

import { fail, redirect } from "@sveltejs/kit";

export const actions: Actions = {

default: async ({ request, cookies }) => {

const formData = await request.formData();

const email = formData.get("email") as string;

const password = formData.get("password") as string;

// Validation

if (!email || !password) {

return fail(400, { email, missing: true });

}

// Authentication

const user = await authenticate(email, password);

if (!user) {

return fail(401, { email, incorrect: true });

}

// Set session cookie

cookies.set("session", user.token, { path: "/" });

throw redirect(303, "/dashboard");

},

};

```

```svelte

{#if form?.missing}

All fields are required

{/if}

{#if form?.incorrect}

Invalid credentials

{/if}

```

API Routes

```typescript

// routes/api/users/+server.ts

import type { RequestHandler } from "./$types";

import { json, error } from "@sveltejs/kit";

export const GET: RequestHandler = async ({ url }) => {

const limit = Number(url.searchParams.get("limit")) || 10;

const users = await prisma.user.findMany({ take: limit });

return json(users);

};

export const POST: RequestHandler = async ({ request }) => {

const body = await request.json();

if (!body.email || !body.name) {

throw error(400, "Missing required fields");

}

const user = await prisma.user.create({ data: body });

return json(user, { status: 201 });

};

// routes/api/users/[id]/+server.ts

export const GET: RequestHandler = async ({ params }) => {

const user = await prisma.user.findUnique({

where: { id: params.id },

});

if (!user) {

throw error(404, "User not found");

}

return json(user);

};

```

Middleware (Hooks)

```typescript

// src/hooks.server.ts

import type { Handle } from "@sveltejs/kit";

export const handle: Handle = async ({ event, resolve }) => {

// Get session from cookie

const session = event.cookies.get("session");

if (session) {

const user = await validateSession(session);

event.locals.user = user;

}

// Protected routes

if (event.url.pathname.startsWith("/dashboard") && !event.locals.user) {

return new Response("Redirect", {

status: 303,

headers: { Location: "/login" },

});

}

return resolve(event);

};

```

---

Event Handling (Svelte 5)

DOM Events (New Syntax)

```svelte

oninput={(e) => name = e.currentTarget.value}

onkeydown={(e) => e.key === 'Enter' && submit()}

/>

```

Component Events (Callback Props)

```svelte

onSelect={(id) => console.log('Selected:', id)}

onClose={() => console.log('Closed')}

/>

```

---

Testing with Vitest

Setup

```typescript

// vitest.config.ts

import { defineConfig } from "vitest/config";

import { svelte } from "@sveltejs/vite-plugin-svelte";

export default defineConfig({

plugins: [svelte({ hot: !process.env.VITEST })],

test: {

globals: true,

environment: "jsdom",

include: ["src/*/.{test,spec}.{js,ts}"],

},

});

```

Component Testing

```typescript

// tests/Counter.test.ts

import { describe, it, expect } from "vitest";

import { render, screen, fireEvent } from "@testing-library/svelte";

import Counter from "$lib/components/Counter.svelte";

describe("Counter", () => {

it("renders initial count", () => {

render(Counter, { props: { initial: 5 } });

expect(screen.getByText("Count: 5")).toBeInTheDocument();

});

it("increments on click", async () => {

render(Counter);

const button = screen.getByRole("button", { name: /increment/i });

await fireEvent.click(button);

expect(screen.getByText("Count: 1")).toBeInTheDocument();

});

});

```

Testing Stores

```typescript

// tests/stores/counter.test.ts

import { describe, it, expect } from "vitest";

import { counter } from "$lib/stores/counter.svelte";

describe("counter store", () => {

it("increments count", () => {

counter.reset();

expect(counter.count).toBe(0);

counter.increment();

expect(counter.count).toBe(1);

counter.increment();

expect(counter.count).toBe(2);

});

it("decrements count", () => {

counter.reset();

counter.decrement();

expect(counter.count).toBe(-1);

});

});

```

---

Migration from Svelte 4

| Svelte 4 | Svelte 5 |

| -------------------------- | ----------------------------------- |

| let count = 0 (reactive) | let count = $state(0) |

| $: doubled = count 2 | let doubled = $derived(count 2) |

| $: console.log(count) | $effect(() => console.log(count)) |

| export let value | let { value } = $props() |

| on:click={handler} | onclick={handler} |

| | {@render children()} |

| createEventDispatcher() | Callback props |

Migration Script

```bash

npx sv migrate svelte-5

```

---

Anti-Patterns to Avoid

| Anti-Pattern | Problem | Solution |

| ---------------------------------- | ---------------------- | --------------------------- |

| Using $effect for derived values | Unnecessary complexity | Use $derived |

| Exporting $state directly | Breaks reactivity | Export getter/setter object |

| Not using TypeScript | Missing type safety | Enable lang="ts" |

| Using old slot syntax | Deprecated in Svelte 5 | Use snippets |

| Using on:event syntax | Deprecated in Svelte 5 | Use onevent props |

---

Performance Benefits

| Metric | Improvement |

| ------------------- | ----------------------------- |

| Bundle size | 40-60% smaller than React/Vue |

| Runtime performance | No virtual DOM overhead |

| Time to interactive | Minimal JavaScript hydration |

| Memory usage | Lower due to compiled output |

---

Related Resources

  • [Svelte 5 Documentation](https://svelte.dev/docs)
  • [SvelteKit Documentation](https://svelte.dev/docs/kit)
  • [Svelte 5 Migration Guide](https://svelte.dev/docs/svelte/v5-migration-guide)
  • [Svelte Testing Docs](https://svelte.dev/docs/svelte/testing)
  • [Joy of Code Tutorials](https://joyofcode.xyz/)

---

When to Use This Skill

  • Building new Svelte 5 applications with runes
  • Migrating from Svelte 4 to Svelte 5
  • Full-stack development with SvelteKit
  • Creating reactive shared state
  • Implementing form actions and API routes
  • Testing Svelte components with Vitest