developing-svelte

jkappers's avatarfrom jkappers

Use when working with .svelte files, SvelteKit projects, or Svelte reactivity. Covers component structure, stores, routing, Svelte 5 runes, and testing patterns.

⭐0stars🔀0forks📁View on GitHub🕐Updated Dec 17, 2025

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.
namedeveloping-svelte
descriptionUse 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

  1. Keep components small; split large ones
  2. Use TypeScript with lang="ts"
  3. Prefer $: over onMount for derived state
  4. Use {#each items as item (item.id)} with keys
  5. Use scoped component styles
  6. Use use:enhance for progressive form enhancement
  7. Colocate load functions with pages