This is a personal fork of sveltejs/svelte-devtools, extended with additional debugging features for Svelte 4 and Svelte 5 applications.
Svelte DevTools is a browser extension for the Svelte framework. It lets you inspect the component tree, reactive state, stores, context, and performance profile directly in the browser DevTools.
^4.0.0 or ^5.0.0dev: true (SvelteKit enables this automatically)Browse the full Svelte component tree. Select any node to inspect its props, state, events, and source location. Use keyboard arrows to navigate the tree.
For Svelte 5 components the panel shows three separate sections: $state runes, $derived runes, and $effect runes.
Every time a component updates, a counter is incremented and displayed at the top of the detail panel (orange badge ⟳ N re-renders). This is a quick way to spot components that are re-rendering more than expected.
Lists all registered stores with their current value and type (writable, readable, derived, custom). Select a store to edit its value in place (writable stores only).
Value history — the last 10 values of each store are recorded with timestamps. Expand the History section in the detail panel to see the change log.
Custom Svelte 5 rune stores are supported as plain objects. The extension will:
subscribe() API exists,restore(), update(), setField() or setX() methods.You can also register read-only computed snapshots:
window.$svelteStores({
bookingSnapshot: {
type: 'custom',
writable: false,
getSnapshot: () => buildBookingSnapshot(),
},
});
If the extension is opened after the app booted, it will also auto-consume window.__svelteStores__ and window.__svelteStoreDeps__ when present.
Visualise how your stores depend on each other. Switch from the list view to the graph view with the ⬡ Graph toggle in the Stores tab header.
Declare dependencies from your application code:
// Call once after your stores are set up
window.$svelteStoreDeps({
total: ['price', 'quantity'],
subtotal: ['price'],
});
Nodes are colour-coded: green = writable, indigo = derived, grey = readable / unregistered. Click a node to highlight its edges.
Inspect values passed through Svelte's setContext / getContext. Register a context group from your component:
import { getContext } from 'svelte';
// Call whenever the context object changes
window.$svelteContext('MyFeature', {
theme: getContext('theme'),
locale: getContext('locale'),
});
The Context tab (purple badge) lists every registered group and its key/value pairs.
Record component update timings to identify performance bottlenecks.
block.p callUsage:
The table shows each component with its update count, total time, average time, and max time. Click any column header to sort. Rows are colour-coded: warm (avg > 16 ms), hot (avg > 50 ms).
Clone and install dependencies:
git clone https://github.com/sveltejs/svelte-devtools.git
cd svelte-devtools
pnpm install
Native binaries for @rollup/rollup-linux-arm64-gnu are not bundled. The project uses a workaround:
@rollup/rollup-linux-arm64-gnu (node_modules/stub/index.js) using acornbuild-vite.mjs with esbuild: falseRun the full build:
# Rollup — builds courier.js and background.js
node node_modules/.pnpm/[email protected]/.../rollup -c
# Vite — builds the panel UI
node build-vite.mjs
Output is in workspace/extension/build/.
chrome://extensionsbuild/ directory┌─────────────────────────────────────────────────────┐
│ Browser page (MAIN world) │
│ courier.js ← injected content script │
│ core.js ← hooks into Svelte internals │
│ profiling.js ← shared profiler module │
└────────────────────┬────────────────────────────────┘
│ window.postMessage / chrome.runtime.sendMessage
┌────────────────────▼────────────────────────────────┐
│ background.js (MV3 service worker) │
│ Relays messages between page and DevTools panel │
│ chrome.runtime.Port ←→ chrome.tabs.sendMessage │
└────────────────────┬────────────────────────────────┘
│ chrome.runtime.Port
┌────────────────────▼────────────────────────────────┐
│ DevTools panel (Svelte 5 SPA — index.html) │
│ App.svelte — tab bar + routing │
│ Inspector.svelte — component tree │
│ StoreInspector.svelte + StoreGraph.svelte │
│ ContextInspector.svelte │
│ Profiler.svelte │
└─────────────────────────────────────────────────────┘
Messages travel over three bridges, each with a specific namespace:
| Prefix | Direction | Purpose |
|---|---|---|
bridge::page/… |
page → panel | Component/store updates from Svelte hooks |
bridge::ext/… |
panel → page | Commands from the panel (select, highlight, profile) |
bridge::courier/… |
internal | Normalised events after routing through background |
bypass::ext/… |
panel → background | Control messages (init, refresh) that stay in the background |
Chrome MV3 service workers are killed after ~30 seconds of inactivity. The panel handles this transparently:
runtime.svelte.ts wraps all port logic in a connect() functionport.onDisconnect, a reconnect is scheduled after 300 msbypass::ext/init so the background re-registers the tab"Extension context invalidated" (extension reloaded mid-session) is caught and handled gracefully — the panel must be reopenedThese functions can be called from your application code to feed data into the DevTools:
// Register store dependencies for the graph view
window.$svelteStoreDeps(deps: Record<string, string[]>): void
// Register a context group for the Context Inspector
window.$svelteContext(label: string, map: Map<string, any>): void
| Feature | Description |
|---|---|
| Horizontal tab bar | Components / Stores / Context / Profiler tabs at the top |
| Re-render counter | Per-component update count in the detail panel |
| Store value history | Last 10 values with timestamps for every store |
| Context Inspector | Inspect setContext/getContext values via $svelteContext() |
| Update Profiler | Record component update timings with sortable table |
| Store Dependency Graph | SVG force-directed graph via $svelteStoreDeps() |
| MV3 manifest fix | Removed legacy background.scripts (MV2 only) |
| Port auto-reconnect | Transparent reconnection when service worker is killed |