Claude Code for Svelte 5: Runes, Snippets, and Fine-Grained Reactivity — Claude Skills 360 Blog
Blog / Frontend / Claude Code for Svelte 5: Runes, Snippets, and Fine-Grained Reactivity
Frontend

Claude Code for Svelte 5: Runes, Snippets, and Fine-Grained Reactivity

Published: January 25, 2027
Read time: 8 min read
By: Claude Skills 360

Svelte 5 replaces the implicit reactivity of Svelte 4 with explicit runes — compiler primitives that make reactivity transparent and composable. $state declares reactive variables. $derived computes values from state. $effect runs side effects when dependencies change. $props defines component inputs with full TypeScript inference. Snippets replace slots with a composable template primitive. Event attributes (onclick) replace the on:click directive. These changes enable fine-grained reactivity — only the DOM nodes depending on changed state update. Claude Code generates Svelte 5 components with runes, SvelteKit route handlers, form actions, and the TypeScript patterns for production Svelte applications.

CLAUDE.md for Svelte 5 Projects

## Svelte 5 Stack
- Version: svelte >= 5.0, @sveltejs/kit >= 2.10
- Runes: $state (reactive), $derived (computed), $effect (side effects), $props (component API)
- Events: onclick/oninput attributes — NOT on:click directives (deprecated)
- Snippets: {#snippet name()}{/snippet} + {@render name()} — replaces slots
- Stores: still work but prefer runes for local state; writable/readable for shared
- Forms: SvelteKit actions with use:enhance for progressive enhancement
- TypeScript: enable with lang="ts" on <script>, type $props with interface

Core Runes

<!-- src/lib/components/OrderList.svelte — Svelte 5 runes -->
<script lang="ts">
  import { orderApi } from '$lib/api'

  interface Order {
    id: string
    status: 'pending' | 'processing' | 'shipped' | 'delivered' | 'cancelled'
    totalCents: number
    createdAt: string
  }

  interface Props {
    customerId: string
    onOrderCancelled?: (orderId: string) => void
  }

  // $props: typed component inputs
  let { customerId, onOrderCancelled }: Props = $props()

  // $state: reactive mutable state
  let orders = $state<Order[]>([])
  let loading = $state(true)
  let error = $state<string | null>(null)
  let searchQuery = $state('')
  let selectedStatus = $state<string>('all')

  // $derived: computed from state, auto-updates
  let filteredOrders = $derived(
    orders.filter(order => {
      const matchesSearch = order.id.includes(searchQuery.toLowerCase())
      const matchesStatus = selectedStatus === 'all' || order.status === selectedStatus
      return matchesSearch && matchesStatus
    })
  )

  let totalSpent = $derived(
    orders.reduce((sum, order) => sum + order.totalCents, 0) / 100
  )

  let pendingCount = $derived(
    orders.filter(o => o.status === 'pending').length
  )

  // $effect: runs after render when dependencies change
  $effect(() => {
    // Re-fetches when customerId changes
    const abortController = new AbortController()

    loading = true
    error = null

    orderApi.list(customerId, { signal: abortController.signal })
      .then(data => {
        orders = data
        loading = false
      })
      .catch(err => {
        if (err.name !== 'AbortError') {
          error = err.message
          loading = false
        }
      })

    // Cleanup: abort on re-run or unmount
    return () => abortController.abort()
  })

  async function cancelOrder(orderId: string) {
    await orderApi.cancel(orderId)
    orders = orders.map(o =>
      o.id === orderId ? { ...o, status: 'cancelled' } : o
    )
    onOrderCancelled?.(orderId)
  }
</script>

<div class="order-list">
  <div class="stats">
    <span>Total: ${totalSpent.toFixed(2)}</span>
    <span>{pendingCount} pending</span>
  </div>

  <div class="filters">
    <!-- onclick attribute replaces on:click -->
    <input
      type="search"
      placeholder="Search orders..."
      bind:value={searchQuery}
    />
    <select bind:value={selectedStatus}>
      <option value="all">All</option>
      <option value="pending">Pending</option>
      <option value="shipped">Shipped</option>
    </select>
  </div>

  {#if loading}
    <p>Loading orders...</p>
  {:else if error}
    <p class="error">{error}</p>
  {:else}
    {#each filteredOrders as order (order.id)}
      <div class="order-card">
        <span class="id">{order.id.slice(-8)}</span>
        <span class="status">{order.status}</span>
        <span class="total">${(order.totalCents / 100).toFixed(2)}</span>
        {#if order.status === 'pending'}
          <button onclick={() => cancelOrder(order.id)}>Cancel</button>
        {/if}
      </div>
    {/each}
  {/if}
</div>

Snippets

<!-- src/lib/components/DataTable.svelte — snippets replace slots -->
<script lang="ts" generics="T">
  interface Props<T> {
    items: T[]
    getKey: (item: T) => string
    // Snippet type: takes T, returns nothing (renderable)
    row: Snippet<[T]>
    emptyState?: Snippet
    header?: Snippet
  }

  import type { Snippet } from 'svelte'

  let {
    items,
    getKey,
    row,
    emptyState,
    header,
  }: Props<T> = $props()
</script>

<table>
  {#if header}
    <thead>{@render header()}</thead>
  {/if}
  <tbody>
    {#if items.length === 0}
      {#if emptyState}
        {@render emptyState()}
      {:else}
        <tr><td>No items</td></tr>
      {/if}
    {:else}
      {#each items as item (getKey(item))}
        {@render row(item)}
      {/each}
    {/if}
  </tbody>
</table>
<!-- Usage: pass snippets as children -->
<DataTable {items} getKey={o => o.id}>
  {#snippet header()}
    <tr><th>ID</th><th>Status</th><th>Total</th></tr>
  {/snippet}

  {#snippet row(order)}
    <tr>
      <td>{order.id.slice(-8)}</td>
      <td>{order.status}</td>
      <td>${(order.totalCents / 100).toFixed(2)}</td>
    </tr>
  {/snippet}

  {#snippet emptyState()}
    <tr><td colspan="3">No orders found</td></tr>
  {/snippet}
</DataTable>

Universal State with Classes

// src/lib/stores/cart.svelte.ts — class-based reactive state
class CartStore {
  items = $state<CartItem[]>([])
  couponCode = $state<string | null>(null)
  couponDiscount = $state(0)

  // $derived inside class
  subtotalCents = $derived(
    this.items.reduce((sum, item) => sum + item.priceCents * item.quantity, 0)
  )

  discountCents = $derived(
    Math.round(this.subtotalCents * this.couponDiscount)
  )

  totalCents = $derived(
    this.subtotalCents - this.discountCents
  )

  itemCount = $derived(
    this.items.reduce((sum, item) => sum + item.quantity, 0)
  )

  addItem(product: Product, quantity = 1) {
    const existing = this.items.find(i => i.productId === product.id)
    if (existing) {
      this.items = this.items.map(i =>
        i.productId === product.id
          ? { ...i, quantity: i.quantity + quantity }
          : i
      )
    } else {
      this.items = [...this.items, {
        productId: product.id,
        name: product.name,
        priceCents: product.priceCents,
        quantity,
      }]
    }
  }

  removeItem(productId: string) {
    this.items = this.items.filter(i => i.productId !== productId)
  }

  async applyCoupon(code: string) {
    const result = await fetch(`/api/coupons/${code}`)
    if (!result.ok) throw new Error('Invalid coupon')
    const { discount } = await result.json()
    this.couponCode = code
    this.couponDiscount = discount
  }

  clear() {
    this.items = []
    this.couponCode = null
    this.couponDiscount = 0
  }
}

// Singleton exported as module — shared across components
export const cart = new CartStore()

SvelteKit Form Actions

// src/routes/checkout/+page.server.ts
import type { Actions, PageServerLoad } from './$types'
import { fail, redirect } from '@sveltejs/kit'
import { z } from 'zod'

export const load: PageServerLoad = async ({ locals }) => {
  const session = await locals.auth.getSession()
  if (!session) throw redirect(302, '/login')

  return { user: session.user }
}

const CheckoutSchema = z.object({
  items: z.string().transform(s => JSON.parse(s)),
  line1: z.string().min(1),
  city: z.string().min(1),
  country: z.string().length(2),
  postalCode: z.string().min(1),
})

export const actions: Actions = {
  placeOrder: async ({ request, locals }) => {
    const session = await locals.auth.getSession()
    if (!session) return fail(401, { error: 'Unauthorized' })

    const formData = Object.fromEntries(await request.formData())
    const parsed = CheckoutSchema.safeParse(formData)

    if (!parsed.success) {
      return fail(422, {
        errors: parsed.error.flatten().fieldErrors,
        values: formData,
      })
    }

    const { items, ...address } = parsed.data

    const order = await orderService.create({
      customerId: session.user.id,
      items,
      shippingAddress: address,
    })

    throw redirect(303, `/orders/${order.id}/confirmation`)
  },
}
<!-- src/routes/checkout/+page.svelte — progressive enhancement -->
<script lang="ts">
  import { enhance } from '$app/forms'
  import type { ActionData, PageData } from './$types'

  let { data, form }: { data: PageData; form: ActionData } = $props()

  let submitting = $state(false)
</script>

<form
  method="POST"
  action="?/placeOrder"
  use:enhance={() => {
    submitting = true
    return async ({ update }) => {
      await update()
      submitting = false
    }
  }}
>
  <input type="hidden" name="items" value={JSON.stringify(cartItems)} />

  <div>
    <label>
      Address
      <input
        name="line1"
        value={form?.values?.line1 ?? ''}
        class:error={form?.errors?.line1}
      />
      {#if form?.errors?.line1}
        <span class="error">{form.errors.line1[0]}</span>
      {/if}
    </label>
  </div>

  <button type="submit" disabled={submitting}>
    {submitting ? 'Placing order...' : 'Place order'}
  </button>
</form>

For the React 19 equivalent with server components and actions providing similar progressive enhancement, see the Next.js App Router guide for server actions. For the SolidJS fine-grained reactivity model that inspired Svelte 5 runes, the SolidJS guide covers signals and effects. The Claude Skills 360 bundle includes Svelte 5 skill sets covering runes, snippets, and SvelteKit form actions. Start with the free tier to try Svelte 5 component generation.

Keep Reading

Frontend

Claude Code for Chart.js Advanced: Custom Plugins and Mixed Charts

Advanced Chart.js patterns with Claude Code — chart.register() for tree-shaking, mixed chart types combining bar and line, custom plugin API with beforeDraw and afterDatasetsDraw hooks, ScriptableContext for computed colors, ChartDataLabels plugin for value labels, chartjs-plugin-zoom for pan and zoom, custom gradient fills via ctx.createLinearGradient, ChartJS annotation plugin for threshold lines, streaming data with chartjs-plugin-streaming, and react-chartjs-2 with useRef and chart instance.

6 min read Jun 27, 2027
Frontend

Claude Code for Nivo: Rich SVG and Canvas Charts

Build rich data visualizations with Nivo and Claude Code — ResponsiveLine and ResponsiveBar for adaptive charts, ResponsiveHeatMap for matrix data, ResponsiveTreeMap for hierarchal data, ResponsiveSunburst for nested proportions, ResponsiveChord for relationship diagrams, ResponsiveCalendar for activity heat maps, ResponsiveNetwork for force graphs, NivoTheme for consistent styling, tooltip customization with sliceTooltip, and motion config for spring animations.

6 min read Jun 26, 2027
Frontend

Claude Code for Victory Charts: React Native and Web Charts

Build cross-platform charts with Victory and Claude Code — VictoryChart, VictoryLine, VictoryBar, and VictoryScatter for web and React Native, VictoryPie for donut charts, VictoryArea for stacked areas, VictoryAxis for custom axes, VictoryTooltip and VictoryVoronoiContainer for hover tooltips, VictoryBrushContainer for range selection, VictoryZoomContainer for pan and zoom, VictoryLegend for series labels, custom theme with VictoryTheme, and VictoryStack for grouped bars.

6 min read Jun 25, 2027

Put these ideas into practice

Claude Skills 360 gives you production-ready skills for everything in this article — and 2,350+ more. Start free or go all-in.

Back to Blog

Get 360 skills free