Skip to content

Advanced Runes

$inspect logs reactive values and re-logs whenever they change — a development-only tool (it becomes a no-op in production builds). Unlike a plain console.log, which fires once, $inspect tracks its arguments and reports every update.

<script>
let count = $state(0);
let doubled = $derived(count * 2);
$inspect(count, doubled); // logs on every change, dev only
</script>

You can chain .with(fn) to run custom logic on each change (for example, to console.trace where a change came from).

Inside a $derived or $effect, every reactive value you read becomes a dependency. Occasionally you want to read a value without creating that dependency — so the effect doesn’t re-run when it changes. untrack (imported from svelte) does that.

<script>
import { untrack } from 'svelte';
let count = $state(0);
let config = $state({ verbose: true });
$effect(() => {
// re-run when count changes, but NOT when config.verbose changes:
const verbose = untrack(() => config.verbose);
if (verbose) console.log(count);
});
</script>

Runes are not limited to .svelte files. In a .svelte.js or .svelte.ts module, you can use $state and $derived to hold reactive state that any component can import — the foundation for app-wide shared state without stores.

counter.svelte.js
export function createCounter() {
let count = $state(0);
return {
get count() { return count; }, // expose via a getter to preserve reactivity
increment() { count++; },
};
}
<script>
import { createCounter } from './counter.svelte.js';
const counter = createCounter();
</script>
<button onclick={counter.increment}>{counter.count}</button>
flowchart LR
  mod[".svelte.js module: $state + getters"] --> a["Component A imports it"]
  mod --> b["Component B imports it"]
  a --> sync["both stay in sync"]
  b --> sync
Shared reactive state lives in a .svelte.js module

The key detail: export access through a getter (or an object/class), not the bare reassigned value. A primitive exported directly would be copied at import time and lose reactivity; a getter reads the live reactive source each time. This pattern — a factory or class in a .svelte.js module — is the modern alternative to stores, covered fully in the State & Stores module. Briefly, $host is a related advanced rune used only when compiling a component to a custom element, to access the host element.

What makes `$inspect(count)` different from `console.log(count)`?
What does `untrack(() => config.verbose)` do inside an effect?
Where can you use runes outside of `.svelte` components?
When sharing `$state` from a module, why expose it through a getter?