Advanced patterns, edge cases, and solutions for complex Svelte scenarios.
<script>
let count = $state(0);
// ❌ This creates an infinite loop
// $effect(() => {
// count++;
// });
// ✅ Use $effect.pre to read before write
$effect.pre(() => {
if (count < 10) {
count++;
}
});
</script><script>
let enabled = $state(true);
let data = $state(null);
// Only compute when enabled
let computed = $derived.by(() => {
if (!enabled) return null;
return data ? data.value * 2 : 0;
});
</script><script>
// Deep reactivity - nested changes trigger updates
let deep = $state({ nested: { value: 0 } });
// Shallow reactivity - only top-level changes trigger updates
let shallow = $state.raw({ nested: { value: 0 } });
function updateDeep() {
deep.nested.value++; // ✅ Triggers reactivity
}
function updateShallow() {
shallow.nested.value++; // ❌ Does NOT trigger reactivity
shallow = { ...shallow, nested: { ...shallow.nested, value: shallow.nested.value + 1 } }; // ✅ Triggers reactivity
}
</script><script>
let state = $state({ count: 0, items: [1, 2, 3] });
function sendToExternalLibrary() {
// External library shouldn't trigger reactivity
const snapshot = $state.snapshot(state);
externalLibrary.process(snapshot);
}
function compareStates() {
const before = $state.snapshot(state);
state.count++;
const after = $state.snapshot(state);
console.log('Changed:', before.count !== after.count);
}
</script><script>
import { onMount } from 'svelte';
onMount(async () => {
const data = await fetch('/api/data').then(r => r.json());
// ⚠️ Cleanup from async onMount is ignored
// Use onDestroy for cleanup instead
return () => {
// This won't run
};
});
import { onDestroy } from 'svelte';
let subscription;
onMount(async () => {
subscription = await setupSubscription();
});
onDestroy(() => {
if (subscription) {
subscription.cleanup();
}
});
</script><script>
import { mount, unmount } from 'svelte';
import Component from './Component.svelte';
let instances = [];
function addComponent() {
const instance = mount(Component, {
target: document.body,
props: { id: instances.length }
});
instances.push(instance);
}
function removeComponent() {
if (instances.length > 0) {
const instance = instances.pop();
unmount(instance);
}
}
</script>import { hydrate } from 'svelte';
import App from './App.svelte';
// Attempt to recover from hydration mismatches
const app = hydrate(App, {
target: document.getElementById('app'),
props: { data: serverData },
recover: true // Enables recovery mode
});import { writable } from 'svelte/store';
const store = writable(0, (set) => {
const interval = setInterval(() => {
set(Math.random());
}, 1000);
// Cleanup when last subscriber unsubscribes
return () => {
clearInterval(interval);
};
});import { derived } from 'svelte/store';
import { asyncData } from './stores.js';
// Show loading state until first value arrives
const processed = derived(
asyncData,
$data => $data ? process($data) : null,
'Loading...' // Initial value shown until first derivation
);import { derived } from 'svelte/store';
import { searchQuery } from './stores.js';
const searchResults = derived(
searchQuery,
($query, set) => {
if (!$query) {
set([]);
return;
}
const controller = new AbortController();
fetch(`/api/search?q=${$query}`, {
signal: controller.signal
})
.then(r => r.json())
.then(set)
.catch(() => set([]));
// Cancel previous request when query changes
return () => controller.abort();
},
[]
);<!-- Parent.svelte -->
<script>
import { setContext } from 'svelte';
setContext('theme', 'dark');
</script>
<slot />
<!-- Child.svelte -->
<script>
import { getContext } from 'svelte';
// Gets 'dark' from parent
const theme = getContext('theme');
</script>
<!-- Nested Child.svelte -->
<script>
import { getContext, setContext } from 'svelte';
// Override parent context
const parentTheme = getContext('theme');
setContext('theme', 'light'); // Children will see 'light'
</script><script>
import { getContext } from 'svelte';
// Provide default if context doesn't exist
const theme = getContext('theme') ?? 'light';
const config = getContext('config') ?? { enabled: true };
</script><script>
import { fade, fly } from 'svelte/transition';
let show = $state(true);
let useFly = $state(false);
</script>
{#if show}
<div transition={useFly ? fly : fade}>
Content
</div>
{/if}<script>
import { fly } from 'svelte/transition';
let show = $state(true);
let direction = $state('down');
let params = $derived({
y: direction === 'down' ? 50 : -50,
duration: 300
});
</script>
{#if show}
<div transition:fly={params}>
Content
</div>
{/if}<script>
import { fly } from 'svelte/transition';
let items = $state([
{ id: 1, name: 'Item 1' },
{ id: 2, name: 'Item 2' },
{ id: 3, name: 'Item 3' }
]);
</script>
{#each items as item (item.id)}
<div transition:fly={{ y: 50 }}>
{item.name}
</div>
{/each}import { render } from 'svelte/server';
import App from './App.svelte';
const { head, body } = render(App, {
props: { data },
csp: {
nonce: 'random-nonce-value'
}
});import { render } from 'svelte/server';
import App from './App.svelte';
const context = new Map();
context.set('user', req.user);
context.set('theme', 'dark');
const { head, body } = render(App, {
props: { data },
context
});import { compile } from 'svelte/compiler';
const source = `
<script>
let count = $state(0);
</script>
<button onclick={() => count++}>
{count}
</button>
`;
const result = compile(source, {
filename: 'Dynamic.svelte',
generate: 'client',
dev: false
});
// Evaluate compiled code
const Component = new Function(result.js.code)();import { preprocess } from 'svelte/compiler';
import sass from 'sass';
const processed = await preprocess(
source,
[
{
style: async ({ content, attributes }) => {
if (attributes.lang === 'scss') {
const result = sass.compileString(content);
return { code: result.css };
}
}
}
],
{ filename: 'Component.svelte' }
);<script lang="ts">
interface Props<T> {
items: T[];
render: (item: T) => string;
}
let { items, render }: Props<any> = $props();
</script>
{#each items as item}
<div>{render(item)}</div>
{/each}<script lang="ts">
import type { EventHandler } from 'svelte/elements';
let handleClick: EventHandler<MouseEvent, HTMLButtonElement> = (e) => {
console.log(e.currentTarget); // Typed as HTMLButtonElement
};
</script>
<button onclick={handleClick}>Click</button><script>
let items = $state.raw([...]); // Use raw for large arrays
// Only update when necessary
let filtered = $derived.by(() => {
// Expensive computation
return items.filter(/* ... */);
});
</script><script>
import { onMount } from 'svelte';
let Component = $state(null);
let loading = $state(true);
onMount(async () => {
const module = await import('./HeavyComponent.svelte');
Component = module.default;
loading = false;
});
</script>
{#if loading}
<p>Loading...</p>
{:else if Component}
<svelte:component this={Component} />
{/if}