Comprehensive usage examples and patterns for common Svelte development scenarios.
<script>
let form = $state({
email: '',
password: '',
errors: {}
});
function validate() {
form.errors = {};
if (!form.email) {
form.errors.email = 'Email is required';
} else if (!form.email.includes('@')) {
form.errors.email = 'Invalid email';
}
if (!form.password) {
form.errors.password = 'Password is required';
} else if (form.password.length < 8) {
form.errors.password = 'Password must be at least 8 characters';
}
return Object.keys(form.errors).length === 0;
}
async function handleSubmit() {
if (!validate()) return;
try {
const response = await fetch('/api/login', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({
email: form.email,
password: form.password
})
});
if (!response.ok) throw new Error('Login failed');
const data = await response.json();
console.log('Logged in:', data);
} catch (error) {
form.errors.submit = error.message;
}
}
</script>
<form onsubmit={(e) => { e.preventDefault(); handleSubmit(); }}>
<input
type="email"
bind:value={form.email}
placeholder="Email"
/>
{#if form.errors.email}
<span class="error">{form.errors.email}</span>
{/if}
<input
type="password"
bind:value={form.password}
placeholder="Password"
/>
{#if form.errors.password}
<span class="error">{form.errors.password}</span>
{/if}
{#if form.errors.submit}
<span class="error">{form.errors.submit}</span>
{/if}
<button type="submit">Login</button>
</form><script>
import { onMount } from 'svelte';
let data = $state(null);
let loading = $state(true);
let error = $state(null);
onMount(async () => {
try {
const response = await fetch('/api/data');
if (!response.ok) throw new Error('Failed to fetch');
data = await response.json();
} catch (e) {
error = e.message;
} finally {
loading = false;
}
});
</script>
{#if loading}
<p>Loading...</p>
{:else if error}
<p class="error">Error: {error}</p>
{:else if data}
<pre>{JSON.stringify(data, null, 2)}</pre>
{/if}<!-- Card.svelte -->
<div class="card">
<div class="card-header">
<slot name="header">Default Header</slot>
</div>
<div class="card-body">
<slot />
</div>
<div class="card-footer">
<slot name="footer" />
</div>
</div>
<!-- Usage -->
<Card>
<svelte:fragment slot="header">
<h2>My Card</h2>
</svelte:fragment>
<p>Card content goes here</p>
<svelte:fragment slot="footer">
<button>Action</button>
</svelte:fragment>
</Card>// stores/user.js
import { writable, derived } from 'svelte/store';
export const user = writable(null);
export const isAuthenticated = derived(user, $user => $user !== null);
export const userName = derived(user, $user => $user?.name ?? 'Guest');<!-- UserProfile.svelte -->
<script>
import { user, isAuthenticated, userName } from './stores/user.js';
</script>
{#if $isAuthenticated}
<p>Welcome, {$userName}!</p>
<button onclick={() => user.set(null)}>Logout</button>
{:else}
<button onclick={() => user.set({ name: 'John' })}>Login</button>
{/if}// stores/api.js
import { readable } from 'svelte/store';
export const apiData = readable(null, (set) => {
let cancelled = false;
fetch('/api/data')
.then(r => r.json())
.then(data => {
if (!cancelled) set(data);
})
.catch(err => {
if (!cancelled) set({ error: err.message });
});
return () => {
cancelled = true;
};
});// stores/cart.js
import { writable, derived } from 'svelte/store';
export const items = writable([]);
export const taxRate = writable(0.1);
export const subtotal = derived(items, $items =>
$items.reduce((sum, item) => sum + item.price * item.quantity, 0)
);
export const tax = derived(
[subtotal, taxRate],
([$subtotal, $taxRate]) => $subtotal * $taxRate
);
export const total = derived(
[subtotal, tax],
([$subtotal, $tax]) => $subtotal + $tax
);<script>
let items = $state([
{ id: 1, name: 'Apple', price: 1.0 },
{ id: 2, name: 'Banana', price: 0.5 },
{ id: 3, name: 'Cherry', price: 2.0 }
]);
let filter = $state('');
let sortBy = $state('name');
let filtered = $derived(
items.filter(item =>
item.name.toLowerCase().includes(filter.toLowerCase())
)
);
let sorted = $derived.by(() => {
const sorted = [...filtered];
sorted.sort((a, b) => {
if (sortBy === 'name') {
return a.name.localeCompare(b.name);
} else {
return a.price - b.price;
}
});
return sorted;
});
</script>
<input bind:value={filter} placeholder="Filter..." />
<select bind:value={sortBy}>
<option value="name">Name</option>
<option value="price">Price</option>
</select>
<ul>
{#each sorted as item (item.id)}
<li>{item.name} - ${item.price}</li>
{/each}
</ul><script>
let count = $state(0);
let intervalId;
$effect(() => {
intervalId = setInterval(() => {
count++;
}, 1000);
return () => {
clearInterval(intervalId);
};
});
</script>
<p>Count: {count}</p><script>
import { windowWidth, windowHeight } from 'svelte/reactivity/window';
let isMobile = $derived($windowWidth < 768);
let orientation = $derived(
$windowWidth > $windowHeight ? 'landscape' : 'portrait'
);
</script>
<p>Width: {$windowWidth}px</p>
<p>Height: {$windowHeight}px</p>
<p>Device: {isMobile ? 'Mobile' : 'Desktop'}</p>
<p>Orientation: {orientation}</p><script>
import { Spring } from 'svelte/motion';
let position = new Spring({ x: 0, y: 0 }, {
stiffness: 0.1,
damping: 0.4
});
function moveTo(x, y) {
position.set({ x, y });
}
</script>
<div
style="transform: translate({$position.current.x}px, {$position.current.y}px)"
onclick={() => moveTo(Math.random() * 400, Math.random() * 400)}
>
Click to move
</div><script>
import { fade, fly } from 'svelte/transition';
let show = $state(true);
let items = $state(['Item 1', 'Item 2', 'Item 3']);
</script>
<button onclick={() => show = !show}>
Toggle
</button>
{#if show}
<div transition:fade={{ duration: 300 }}>
<h2>Content</h2>
</div>
{/if}
{#each items as item, i (i)}
<div transition:fly={{ y: 50, duration: 300 }}>
{item}
</div>
{/each}// server.js
import express from 'express';
import { render } from 'svelte/server';
import App from './App.svelte';
const app = express();
app.get('/', (req, res) => {
const { head, body } = render(App, {
props: {
user: req.user,
data: getInitialData()
}
});
res.send(`
<!DOCTYPE html>
<html>
<head>${head}</head>
<body>${body}</body>
</html>
`);
});// client.js
import { hydrate } from 'svelte';
import App from './App.svelte';
const app = hydrate(App, {
target: document.getElementById('app'),
props: {
user: window.__INITIAL_USER__,
data: window.__INITIAL_DATA__
}
});<!-- ThemeProvider.svelte -->
<script>
import { setContext } from 'svelte';
let { theme = 'light' } = $props();
setContext('theme', {
current: $derived(theme),
toggle: () => {
theme = theme === 'light' ? 'dark' : 'light';
}
});
</script>
<div class="theme-{theme}">
<slot />
</div>
<!-- Usage -->
<script>
import { getContext } from 'svelte';
const theme = getContext('theme');
</script>
<button onclick={theme.toggle}>
Current theme: {$theme.current}
</button>// actions/clickOutside.js
export function clickOutside(node, callback) {
function handleClick(event) {
if (!node.contains(event.target)) {
callback(event);
}
}
document.addEventListener('click', handleClick, true);
return {
destroy() {
document.removeEventListener('click', handleClick, true);
}
};
}<script>
import { clickOutside } from './actions/clickOutside.js';
let show = $state(false);
</script>
<div use:clickOutside={() => show = false}>
<button onclick={() => show = !show}>Toggle</button>
{#if show}
<div class="dropdown">Content</div>
{/if}
</div>// actions/autoResize.js
export function autoResize(node) {
function resize() {
node.style.height = 'auto';
node.style.height = node.scrollHeight + 'px';
}
resize();
node.addEventListener('input', resize);
return {
update() {
resize();
},
destroy() {
node.removeEventListener('input', resize);
}
};
}<script>
import { autoResize } from './actions/autoResize.js';
let text = $state('');
</script>
<textarea bind:value={text} use:autoResize></textarea>