Svelte 5 introduces runes - a new reactivity system replacing the old `$:` syntax.
Svelte 5 introduces runes for reactivity, replacing `$:` syntax. Use this reference when migrating from Svelte 4 or writing new Svelte 5 code to quickly find the correct patterns for `$state`, `$derived`, `$effect`, `$props`, and snippets.
/plugin marketplace add linehaul-ai/linehaulai-claude-marketplace/plugin install gh-issue@linehaulai-claude-marketplaceinit-claude-docs/references/Svelte 5 introduces runes - a new reactivity system replacing the old $: syntax.
<script>
// $state - reactive state
let count = $state(0);
let user = $state({ name: '', email: '' });
// $derived - computed values (replaces $:)
let doubled = $derived(count * 2);
let isValid = $derived(user.name.length > 0 && user.email.includes('@'));
// $effect - side effects (replaces $: with side effects)
$effect(() => {
console.log('Count changed:', count);
// Cleanup function (optional)
return () => console.log('Cleanup');
});
// $props - component props
let { title, onSubmit } = $props();
// $bindable - two-way bindable props
let { value = $bindable() } = $props();
</script>
<!-- Avoid: Direct mutation tracking issues -->
<script>
let items = $state([]);
items.push(newItem); // Works in Svelte 5!
</script>
<!-- Object state -->
<script>
let form = $state({
name: '',
email: '',
errors: {}
});
function updateField(field, value) {
form[field] = value;
}
</script>
<script>
let {
variant = 'primary',
size = 'md',
disabled = false,
onclick
} = $props();
</script>
<!-- Parent.svelte -->
<Card>
{#snippet header()}
<h2>Title</h2>
{/snippet}
{#snippet footer()}
<Button>Action</Button>
{/snippet}
<p>Main content</p>
</Card>
<!-- Card.svelte -->
<script>
let { header, footer, children } = $props();
</script>
<div class="card">
{#if header}
<header>{@render header()}</header>
{/if}
<main>{@render children()}</main>
{#if footer}
<footer>{@render footer()}</footer>
{/if}
</div>
<script>
let { onclick, onsubmit } = $props();
function handleClick(event) {
// Process then delegate
onclick?.(event);
}
</script>
<button {onclick}>Click me</button>
<!-- Or with handler -->
<button onclick={handleClick}>Click me</button>
// +page.ts (universal load)
export async function load({ fetch, params }) {
const response = await fetch(`/api/items/${params.id}`);
return { item: await response.json() };
}
// +page.server.ts (server-only load)
export async function load({ locals, params }) {
// Access server-only resources
const item = await db.items.findUnique({ where: { id: params.id } });
return { item };
}
// +page.server.ts
export const actions = {
default: async ({ request, locals }) => {
const data = await request.formData();
// Process form
return { success: true };
},
delete: async ({ params }) => {
await db.items.delete({ where: { id: params.id } });
throw redirect(303, '/items');
}
};
// +error.svelte
<script>
import { page } from '$app/stores';
</script>
<h1>{$page.status}: {$page.error?.message}</h1>
<style>
/* Scoped to this component */
.button { }
/* Global escape hatch */
:global(.external-class) { }
</style>
<div class="themed" style:--primary={primaryColor}>
<button class="btn">Themed button</button>
</div>
<style>
.btn {
background: var(--primary, #007bff);
}
</style>
src/
├── lib/
│ ├── components/ # Reusable components
│ │ ├── ui/ # Base UI components
│ │ └── features/ # Feature-specific components
│ ├── stores/ # Svelte stores
│ ├── utils/ # Utility functions
│ └── types/ # TypeScript types
├── routes/
│ ├── +layout.svelte # Root layout
│ ├── +page.svelte # Home page
│ └── [slug]/ # Dynamic routes
└── app.html # HTML template
arr.push(item) is reactive