developing-svelte
Use when working with .svelte files, SvelteKit projects, or Svelte reactivity. Covers component structure, stores, routing, Svelte 5 runes, and testing patterns.
When & Why to Use This Skill
This Claude skill serves as a specialized assistant for Svelte and SvelteKit development, providing optimized code patterns and architectural guidance. It streamlines the creation of reactive components, manages complex state through stores, and implements robust SvelteKit routing and server-side logic. With comprehensive support for Svelte 5 runes ($state, $derived, $effect) and TypeScript integration, it ensures modern, type-safe, and high-performance frontend engineering.
Use Cases
- Developing modern web interfaces using Svelte 5 runes to manage fine-grained reactivity and component props.
- Architecting scalable SvelteKit applications with file-based routing, layouts, and server-side data fetching (+page.server.ts).
- Implementing complex state management solutions using writable, derived stores, and the Context API for cross-component communication.
- Building robust forms with progressive enhancement using SvelteKit form actions and the use:enhance directive.
- Writing unit and integration tests for Svelte components using Vitest and Svelte Testing Library to ensure UI reliability.
| name | developing-svelte |
|---|---|
| description | Use when working with .svelte files, SvelteKit projects, or Svelte reactivity. Covers component structure, stores, routing, Svelte 5 runes, and testing patterns. |
Svelte Development
Apply these patterns when working with Svelte components and SvelteKit applications.
Component Structure
<script lang="ts">
import { onMount } from 'svelte';
import Component from './Component.svelte';
export let propName: string;
export let optionalProp: number = 0;
let count = 0;
$: doubled = count * 2;
function increment() {
count += 1;
}
onMount(() => {
return () => { /* cleanup */ };
});
</script>
<div class="container">
<h1>{propName}</h1>
<button on:click={increment}>Count: {count}</button>
</div>
<style>
.container { padding: 1rem; }
</style>
Reactivity
<script>
let count = 0;
$: doubled = count * 2;
$: if (count > 10) alert('High count!');
</script>
Store Subscriptions
<script>
import { writable, derived } from 'svelte/store';
const count = writable(0);
const doubled = derived(count, $count => $count * 2);
function increment() {
$count += 1;
}
</script>
<p>Count: {$count}, Doubled: {$doubled}</p>
Stores
Writable Store
import { writable } from 'svelte/store';
function createCounter() {
const { subscribe, set, update } = writable(0);
return {
subscribe,
increment: () => update(n => n + 1),
reset: () => set(0)
};
}
export const counter = createCounter();
Derived Store
import { derived } from 'svelte/store';
import { items } from './items';
export const totalPrice = derived(items, $items =>
$items.reduce((sum, item) => sum + item.price, 0)
);
Context vs Stores
Use stores for state shared across unrelated components. Use context for state scoped to a component tree.
<script>
import { setContext, getContext } from 'svelte';
setContext('theme', { color: 'dark' });
const theme = getContext('theme');
</script>
SvelteKit
File-based Routing
src/routes/
├── +page.svelte # /
├── +layout.svelte
├── about/+page.svelte # /about
├── blog/[slug]/+page.svelte # /blog/:slug
└── api/users/+server.ts # /api/users
Page Load
// +page.server.ts
import type { PageServerLoad } from './$types';
export const load: PageServerLoad = async ({ params, fetch }) => {
const res = await fetch(`/api/posts/${params.slug}`);
return { post: await res.json() };
};
<!-- +page.svelte -->
<script lang="ts">
import type { PageData } from './$types';
export let data: PageData;
</script>
<h1>{data.post.title}</h1>
Form Actions
// +page.server.ts
import type { Actions } from './$types';
import { fail, redirect } from '@sveltejs/kit';
export const actions: Actions = {
default: async ({ request }) => {
const data = await request.formData();
const email = data.get('email');
if (!email) return fail(400, { email, missing: true });
throw redirect(303, '/success');
}
};
<script>
import { enhance } from '$app/forms';
</script>
<form method="POST" use:enhance>
<input name="email" type="email" />
<button>Submit</button>
</form>
Svelte 5 Runes
<script>
let count = $state(0);
let doubled = $derived(count * 2);
$effect(() => {
console.log('count changed:', count);
});
let { name, value = 0 }: { name: string; value?: number } = $props();
</script>
| Svelte 4 | Svelte 5 |
|---|---|
let count = 0 |
let count = $state(0) |
$: doubled = count * 2 |
let doubled = $derived(count * 2) |
$: { ... } |
$effect(() => { ... }) |
export let prop |
let { prop } = $props() |
Testing
import { render, screen, fireEvent } from '@testing-library/svelte';
import { describe, it, expect } from 'vitest';
import Counter from './Counter.svelte';
describe('Counter', () => {
it('increments count on click', async () => {
render(Counter);
const button = screen.getByRole('button');
expect(button).toHaveTextContent('Count: 0');
await fireEvent.click(button);
expect(button).toHaveTextContent('Count: 1');
});
});
Requirements
- Keep components small; split large ones
- Use TypeScript with
lang="ts" - Prefer
$:overonMountfor derived state - Use
{#each items as item (item.id)}with keys - Use scoped component styles
- Use
use:enhancefor progressive form enhancement - Colocate load functions with pages