Documentation Index
Fetch the complete documentation index at: https://mintlify.com/sveltejs/svelte/llms.txt
Use this file to discover all available pages before exploring further.
Svelte provides excellent debugging tools and techniques to help you identify and fix issues in your applications.
The official browser extension provides powerful debugging capabilities:
Installation
Features
Component Tree:
- View component hierarchy
- Inspect component props and state
- See which components are mounted
- Track component parent-child relationships
State Inspector:
- View all reactive state (
$state)
- Inspect derived values (
$derived)
- Monitor state changes in real-time
- Modify state values directly
Event Timeline:
- Track component lifecycle events
- See when effects run
- Monitor prop updates
Enable Svelte DevTools in development mode for the best debugging experience. It automatically detects Svelte applications.
The $inspect Rune
Basic Usage
Log reactive state changes with $inspect:
<script>
let count = $state(0);
let user = $state({ name: 'Alice', age: 30 });
// Logs whenever count or user changes
$inspect(count, user);
</script>
<button onclick={() => count++}>Count: {count}</button>
<button onclick={() => user.age++}>Age: {user.age}</button>
Output:
count: 1
user: { name: 'Alice', age: 31 }
at Component.svelte:5:2
$inspect only works in development mode. It becomes a no-op in production builds.
Deep Reactivity Tracking
$inspect tracks changes deeply in objects and arrays:
<script>
let todos = $state([
{ id: 1, text: 'Learn Svelte', done: false },
{ id: 2, text: 'Build app', done: false }
]);
$inspect(todos);
function toggleTodo(id) {
const todo = todos.find(t => t.id === id);
todo.done = !todo.done; // $inspect logs this mutation
}
</script>
Custom Logging with .with
Provide your own logging function:
<script>
let count = $state(0);
$inspect(count).with((type, value) => {
if (type === 'update') {
console.log(`Count changed to ${value}`);
debugger; // Pause execution on changes
}
});
</script>
Parameters:
type: "init" (first call) or "update" (subsequent changes)
...values: The values passed to $inspect
Tracing Reactive Dependencies
Use $inspect.trace() to debug why effects or derived values re-run:
<script>
let firstName = $state('Alice');
let lastName = $state('Smith');
let age = $state(30);
let greeting = $derived.by(() => {
$inspect.trace('greeting calculation');
return `${firstName} ${lastName} (${age})`;
});
</script>
<input bind:value={firstName} placeholder="First name" />
<input bind:value={lastName} placeholder="Last name" />
<input bind:value={age} type="number" />
<p>{greeting}</p>
Console output when firstName changes:
greeting calculation
Dependencies:
- firstName (changed)
- lastName
- age
$inspect.trace() must be the first statement in an effect or derived function body.
Elements Panel:
- Inspect DOM structure
- View component-generated HTML
- Modify styles in real-time
- Check element accessibility
Console:
<script>
let data = $state({ items: [] });
// Make available in console for debugging
globalThis.debug = { data };
$effect(() => {
console.log('Data updated:', data);
});
</script>
Now in console:
> debug.data
{ items: [...] }
> debug.data.items.push({ id: 4 })
// Triggers reactive updates
Performance Panel:
- Start recording
- Interact with your app (click, scroll, type)
- Stop recording
- Analyze flame graph for slow operations
- Identify expensive component renders
Network Panel:
- Monitor API requests
- Check request/response times
- Debug failed requests
- Inspect headers and payloads
Source Maps
Svelte generates source maps automatically in development:
// vite.config.js
export default {
build: {
sourcemap: true // Enable in production for debugging
}
};
With source maps, you can:
- Set breakpoints in
.svelte files
- Step through original source code
- See original variable names
- Get accurate stack traces
Common Debugging Scenarios
Reactivity Not Working
Problem: Component doesn’t update when state changes
<script>
// ❌ Bad: Not reactive
let count = 0;
function increment() {
count++; // DOM won't update
}
</script>
<button onclick={increment}>{count}</button>
Solution: Use $state
<script>
// ✅ Good: Reactive
let count = $state(0);
function increment() {
count++; // DOM updates
}
</script>
Debug with $inspect.trace:
<script>
let count = $state(0);
$effect(() => {
$inspect.trace('count effect');
console.log('Count:', count);
});
</script>
Props Not Updating
Problem: Child component doesn’t react to prop changes
<!-- Child.svelte -->
<script>
let { value } = $props();
// ❌ Bad: Captures initial value only
let doubled = value * 2;
</script>
<p>{doubled}</p>
Solution: Use $derived for computed props
<script>
let { value } = $props();
// ✅ Good: Recalculates when value changes
let doubled = $derived(value * 2);
</script>
Infinite Loops in Effects
Problem: Effect triggers itself
<script>
let items = $state([]);
$effect(() => {
// ❌ Bad: Modifies its own dependency
items = [...items, Math.random()];
});
</script>
Solution: Use the right tool
<script>
let count = $state(0);
// ✅ For initial setup only
$effect(() => {
const interval = setInterval(() => {
count++; // This is fine - effect doesn't depend on count
}, 1000);
return () => clearInterval(interval);
});
</script>
Debug infinite loops:
<script>
let data = $state([]);
let runCount = 0;
$effect(() => {
console.log('Effect run #', ++runCount);
if (runCount > 10) {
debugger; // Pause after 10 runs
}
// ... effect code
});
</script>
Component Not Rendering
Problem: Component instance doesn’t appear in DOM
Check:
- Import path: Is the component imported correctly?
- Component name: Does it start with a capital letter?
- Conditional rendering: Is it inside a falsy
{#if} block?
- Parent mount: Is the parent component mounted?
<script>
import MyComponent from './MyComponent.svelte'; // Check path
let show = $state(true);
$effect(() => {
console.log('Parent mounted');
});
</script>
{#if show}
<MyComponent /> <!-- Capital letter required -->
{:else}
<p>Component hidden</p>
{/if}
Error Boundaries
Catch and handle component errors gracefully:
<script>
import { ErrorBoundary } from './ErrorBoundary.svelte';
import RiskyComponent from './RiskyComponent.svelte';
</script>
<svelte:boundary onerror={(error) => console.error(error)}>
<RiskyComponent />
{#snippet failed(error)}
<div class="error">
<h2>Something went wrong</h2>
<pre>{error.message}</pre>
</div>
{/snippet}
</svelte:boundary>
Use <svelte:boundary> to prevent errors from crashing your entire app.
Testing and Debugging
Component Tests with Vitest
Write tests that act as debugging documentation:
import { render, screen } from '@testing-library/svelte';
import { expect, test } from 'vitest';
import Counter from './Counter.svelte';
test('counter increments', async () => {
const { component } = render(Counter, { props: { initial: 0 } });
const button = screen.getByRole('button');
expect(button).toHaveTextContent('0');
await button.click();
expect(button).toHaveTextContent('1');
});
Debug Mode in Tests
import { render } from '@testing-library/svelte';
import { expect, test } from 'vitest';
import Component from './Component.svelte';
test.only('debug specific test', () => {
const { debug } = render(Component);
debug(); // Prints current DOM
// ... test assertions
});
Logging Best Practices
Structured Logging
<script>
function fetchData() {
console.group('fetchData');
console.time('fetch-duration');
fetch('/api/data')
.then(r => r.json())
.then(data => {
console.log('Data:', data);
console.timeEnd('fetch-duration');
console.groupEnd();
})
.catch(error => {
console.error('Fetch failed:', error);
console.groupEnd();
});
}
</script>
Conditional Logging
<script>
const DEBUG = import.meta.env.DEV;
function log(...args) {
if (DEBUG) console.log(...args);
}
let count = $state(0);
$effect(() => {
log('Count changed:', count);
});
</script>
Development vs Production
Environment Detection
<script>
import { DEV } from 'esm-env';
if (DEV) {
console.log('Development mode');
globalThis.debugApp = {
// Expose debugging utilities
};
}
</script>
Debug Panels
<script>
import { DEV } from 'esm-env';
import DebugPanel from './DebugPanel.svelte';
let showDebug = $state(false);
</script>
{#if DEV}
<button onclick={() => showDebug = !showDebug}>
Toggle Debug Panel
</button>
{#if showDebug}
<DebugPanel />
{/if}
{/if}
Debugging Tips
- Use
$inspect liberally - It’s removed in production automatically
- Enable Svelte DevTools - Essential for understanding component state
- Add
debugger statements - Pause execution at specific points
- Log component lifecycle - Track when components mount/unmount
- Test edge cases - Write tests for problematic scenarios
- Use TypeScript - Catch type errors before runtime
- Check the compiler warnings - Svelte warns about common mistakes
- Profile performance - Use browser tools to find bottlenecks
Common mistakes to avoid:
- Forgetting to use
$state for reactive variables
- Using
$effect instead of $derived for computed values
- Not providing keys in
{#each} blocks
- Modifying props directly (use callbacks or events)
- Ignoring compiler warnings
Additional Resources
The best debugging tool is prevention. Use TypeScript, write tests, and pay attention to compiler warnings to catch issues early.