Skill v1.0.0
currentAutomated scan96/100version: "1.0.0" name: svelte-builder description: | Expert guidance for building applications with Svelte 5 and SvelteKit, covering runes reactivity, component patterns, stores, SSR/SSG, and production deployment. Use when the user asks about svelte builder, svelte builder best practices, or needs guidance on svelte builder implementation. Do NOT use when the user needs a different specialized skill or is asking about an unrelated technology domain. license: Apache-2.0 metadata: author: foundry-skills version: "1.0.0" tags: "web-development frontend javascript" category: "web-development" subcategory: "frontend-frameworks" depends: "" disclaimer: "none" difficulty: "intermediate"
Svelte Builder
You are an expert Svelte and SvelteKit developer who builds performant, accessible web applications. You guide developers through Svelte's compiler-first approach, the runes reactivity system, component architecture, server-side rendering, and production deployment patterns. You prioritize Svelte 5 conventions while noting migration paths from Svelte 4.
Svelte 5 Runes Reactivity
Core Runes
<script> .// $state - reactive state declarationlet count = $state(0);let items = $state([]);let user = $state({ name: '', email: '' });// $derived - computed values (replaces $: reactive statements)let doubled = $derived(count * 2);let total = $derived(items.reduce((sum, i) => sum + i.price, 0));// $derived.by - complex derivationslet filtered = $derived.by(() => {return items.filter(i => i.active).sort((a, b) => a.name.localeCompare(b.name));});// $effect - side effects (replaces $: side-effect statements)$effect(() => {document.title = `Count: ${count}`;// cleanup function (optional)return () => console.log('cleanup');});// $effect.pre - runs before DOM updates$effect.pre(() => {// scroll preservation, measurement, etc.});</script>
Props with $props and $bindable
<script> .// Destructured props with defaultslet { title, variant = 'primary', children, ...rest } = $props();// Bindable props (two-way binding)let { value = $bindable(''), open = $bindable(false) } = $props();</script><div class="card card--{variant}" {...rest}><h2>{title}</h2>{@render children()}</div>
Component Patterns
Snippet-Based Composition (Svelte 5)
<!-- Parent.svelte --><script>import DataTable from './DataTable.svelte';let users = $state([]);</script>{#snippet headerCell(column)}<th class="sortable">{column.label}</th>{/snippet}{#snippet bodyCell(row, column)}{#if column.key === 'avatar'}<img src={row.avatar} alt={row.name} width="32" />{:else}{row[column.key]}{/if}{/snippet}<DataTable data={users} {headerCell} {bodyCell} />
<!-- DataTable.svelte --><script>let { data, columns, headerCell, bodyCell } = $props();</script><table><thead><tr>{#each columns as col}{@render headerCell(col)}{/each}</tr></thead><tbody>{#each data as row (row.id)}<tr>{#each columns as col}{@render bodyCell(row, col)}{/each}</tr>{/each}</tbody></table>
Reusable Action Pattern
<script> ./** Click outside detection action */function clickOutside(node, callback) {function handleClick(event) {if (!node.contains(event.target)) callback();}document.addEventListener('click', handleClick, true);return {destroy() {document.removeEventListener('click', handleClick, true);}};}/** Intersection observer action */function inView(node, params = {}) {const { threshold = 0.5, once = true } = params;const observer = new IntersectionObserver(([entry]) => {if (entry.isIntersecting) {node.dispatchEvent(new CustomEvent('inview'));if (once) observer.unobserve(node);}}, { threshold });observer.observe(node);return { destroy() { observer.disconnect(); } };}</script><div use:clickOutside={() => open = false}><!-- dropdown content --></div><div use:inView={{ threshold: 0.3 }} oninview={loadMore}><!-- lazy loaded section --></div>
SvelteKit Application Architecture
Route Structure
src/routes/+layout.svelte # Root layout+layout.server.ts # Root layout data (auth, theme)+page.svelte # Home page+error.svelte # Error boundary(marketing)/ # Route group (no URL segment)+layout.svelteabout/+page.sveltepricing/+page.svelte(app)/ # Authenticated route group+layout.server.ts # Auth guarddashboard/+page.svelte+page.server.ts # Dashboard data loadersettings/+page.svelte+page.server.tsapi/users/+server.ts # API endpoint: GET, POST[id]/+server.ts # API endpoint: GET, PUT, DELETE
Data Loading Patterns
// +page.server.ts - Server-only data loadingimport type { PageServerLoad } from './$types';import { error, redirect } from '@sveltejs/kit';export const load: PageServerLoad = async ({ params, locals, get, depends }) => {if (!locals.user) throw redirect(303, '/login');depends('app:posts'); // invalidation keyconst [posts, categories] = await Promise.all([get(`/api/posts?author=${locals.user.id}`).then(r => r.json()),get('/api/categories').then(r => r.json())]);if (!posts) throw error(404, 'No posts found');return { posts, categories, user: locals.user };};
<!-- +page.svelte --><script>let { data } = $props();// data.posts, data.categories, data.user are typed automatically</script>
Form Actions
// +page.server.tsimport type { Actions } from './$types';import { fail } from '@sveltejs/kit';export const actions: Actions = {create: async ({ request, locals }) => {const formData = await request.formData();const title = formData.get('title')?.toString().trim();const body = formData.get('body')?.toString().trim();if (!title || title.length < 3) {return fail(400, { title, body, error: 'Title must be at least 3 characters' });}const post = await locals.db.post.create({ data: { title, body, authorId: locals.user.id } });return { success: true, id: post.id };},delete: async ({ request, locals }) => {const formData = await request.formData();const id = formData.get('id')?.toString();await locals.db.post.delete({ where: { id, authorId: locals.user.id } });return { success: true };}};
<!-- +page.svelte --><script>import { enhance } from '$app/forms';let { form } = $props();</script><form method="POST" action="?/create" use:enhance={() => {return async ({ update }) => {await update({ reset: false });};}}><input name="title" value={form?.title ?? ''} />{#if form?.error}<span class="error">{form.error}</span>{/if}<textarea name="body">{form?.body ?? ''}</textarea><button type="submit">Create Post</button></form>
Stores and Shared State
Svelte 5 Shared State with Classes
// lib/stores/cart.svelte.tsclass CartStore {items = $state<CartItem[]>([]);get count() { return this.items.length; }get total() { return this.items.reduce((sum, i) => sum + i.price * i.qty, 0); }add(product: Product, qty = 1) {const existing = this.items.find(i => i.id === product.id);if (existing) {existing.qty += qty;} else {this.items.push({ ...product, qty });}}remove(id: string) {this.items = this.items.filter(i => i.id !== id);}clear() { this.items = []; }}export const cart = new CartStore();
Context API for Component Trees
<!-- ThemeProvider.svelte --><script>import { setContext } from 'svelte';let { theme = 'light', children } = $props();let current = $state(theme);setContext('theme', {get current() { return current; },toggle() { current = current === 'light' ? 'dark' : 'light'; }});</script>{@render children()}
<!-- ThemedButton.svelte --><script>import { getContext } from 'svelte';const theme = getContext('theme');</script><button class="btn btn--{theme.current}" onclick={theme.toggle}>Toggle Theme</button>
SSR, SSG, and Rendering Strategies
Per-Route Rendering Control
// +page.ts - Client-side renderingexport const ssr = false;export const csr = true;// +page.ts - Static pre-renderingexport const prerender = true;// +page.server.ts - Dynamic with streamingexport const load = async ({ get }) => {return {quickData: await get('/api/quick').then(r => r.json()),// Streamed (resolves after initial HTML)slowData: get('/api/slow').then(r => r.json())};};
Adapter Configuration
// svelte.config.jsimport adapter from '@sveltejs/adapter-node'; // Node server// import adapter from '@sveltejs/adapter-static'; // Static site// import adapter from '@sveltejs/adapter-vercel'; // Vercel// import adapter from '@sveltejs/adapter-cloudflare'; // Cloudflareexport default {kit: {adapter: adapter({ out: 'build', precompress: true }),alias: {$components: 'src/lib/components',$stores: 'src/lib/stores',$utils: 'src/lib/utils'},prerender: {handleHttpError: 'warn',entries: ['*', '/sitemap.xml']}}};
Performance Checklist
- [ ] Use
{#key}blocks only when necessary to force re-creation - [ ] Apply
{#each items as item (item.id)}keyed each blocks for list stability - [ ] Prefer
$derivedover$effectfor computed values (avoids extra render cycles) - [ ] Use
$effect.prefor DOM measurements before paint - [ ] Lazy load routes with dynamic imports in hooks
- [ ] Stream slow data promises from server load functions
- [ ] Set
prerender = trueon static pages - [ ] Use
precompress: truein adapter config for gzip/brotli - [ ] Inline critical CSS with
vitePreprocessconfiguration - [ ] Add
loading="lazy"and explicit dimensions to images - [ ] Use the
{@html}tag sparingly and sanitize content - [ ] Profile with Svelte DevTools browser extension
Migration from Svelte 4 to Svelte 5
| Svelte 4 | Svelte 5 | |
|---|---|---|
export let prop | let { prop } = $props() | |
$: derived = x * 2 | let derived = $derived(x * 2) | |
$: { sideEffect() } | $effect(() => { sideEffect() }) | |
<slot /> | {@render children()} | |
<slot name="header" /> | {@render header()} | |
$$restProps | let { ...rest } = $props() | |
createEventDispatcher | callback props | |
| Writable stores | Class with $state fields |
When to Use
Use this skill when:
- Designing or implementing svelte builder solutions
- Reviewing or improving existing svelte builder approaches
- Making architectural or implementation decisions about svelte builder
- Learning svelte builder patterns and best practices
- Troubleshooting svelte builder-related issues
Do NOT use this skill when:
- The question is about a fundamentally different technology domain
- A more specific sibling skill covers the exact topic needed
- The user needs a complete hands-on tutorial rather than expert guidance
Output Format
# Svelte Builder Analysis## Context Assessment[Situation summary and constraints]## Recommended Approach[Primary recommendation with rationale]## Implementation Steps1.[Step with specific details]2.[Step with specific details]3.[Step with specific details]## Trade-offs and Considerations-[Key trade-off 1]-[Key trade-off 2]## Next Steps-[Immediate action item]-[Follow-up action item]
Example
Input: "Help me implement svelte builder for a medium-scale production application"
Output: A structured analysis covering current state assessment, recommended svelte builder approach with specific patterns, implementation roadmap with milestones, and risk mitigation strategies tailored to the application scale and constraints.
Edge Cases
- Legacy system integration: When svelte builder must coexist with legacy approaches, provide a gradual migration path rather than a complete rewrite
- Scale mismatch: When the solution complexity exceeds the project scale, recommend a simpler approach and note when to revisit
- Team skill gaps: When the team lacks experience with the recommended approach, include learning resources and simpler alternatives
- Conflicting requirements: When constraints conflict (e.g., performance vs. maintainability), explicitly state the trade-off and recommend based on stated priorities