redux-devtools

Every committed change to a named atom is sent as an action; the extension's time-travel / commit / reset controls restore state back onto the store.

Requires the extension
Install the Redux DevTools browser extension. Without it, connectReduxDevtools warns once and returns a no-op handle.

Install

bun add @valdres/redux-devtools

Usage

Call connectReduxDevtools(store) once on mount; call disconnect() on teardown.

<script>
import { onMount } from "svelte"
import { getValdresContext } from "valdres-svelte"
import { connectReduxDevtools } from "@valdres/redux-devtools"

const store = getValdresContext()
onMount(() => connectReduxDevtools(store).disconnect)
</script>

Vanilla: pass the store instance directly.

import { store } from "valdres"
import { connectReduxDevtools } from "@valdres/redux-devtools"

const handle = connectReduxDevtools(store())
// handle.disconnect() to detach

Options

connectReduxDevtools(store, options?) accepts:

OptionTypeDefaultNotes
namestring"valdres"Instance name in the extension dropdown.
serialize(state, value) => unknownidentityShape values that aren't structured-cloneable before they're sent.
scopesbooleantrueInclude scope state under @scopes and react to scope writes.
unnamed"track" | "ignore""track"track labels unnamed atoms unnamed_atom_N; ignore omits them.
excludeExcludeRule | ExcludeRule[]Leave atoms/selectors out entirely — neither seeded nor reported. A rule is an atom/selector reference, an atomFamily/selectorFamily (all its members), a name string, or a predicate (state) => boolean.
selectorsbooleanfalseAlso report named selectors under @computed (display-only, never restored).

Excluding high-frequency atoms

A single atom that updates on every frame — a cursor position, pointer coordinates, a scroll offset — floods the timeline and buries the actions you actually care about. Pass it to exclude and it disappears from DevTools while your store keeps working normally:

connectReduxDevtools(store, {
  // any of: a reference, a name, an atomFamily/selectorFamily, a predicate, or a mix
  exclude: [cursorPositionAtom, "pointerAtom", pointerFamily],
})

Exports

ExportKindType
connectReduxDevtoolsutil fn(store: Store, options?: ConnectReduxDevtoolsOptions) => ReduxDevtoolsHandle
ConnectReduxDevtoolsOptionstype{ name?, serialize?, scopes?, unnamed?, exclude?, selectors? }
ExcludeOption / ExcludeRuletypeatom/selector/family reference, name string, or (state) => boolean
ReduxDevtoolsHandletype{ disconnect(): void }
DevtoolsSnapshottypeRecord<string, unknown> & { "@scopes"?, "@computed"? }
ReduxDevtoolsConnectiontypeextension connection surface
ReduxDevtoolsExtensiontypewindow.__REDUX_DEVTOOLS_EXTENSION__ surface
ReduxDevtoolsMessagetypemessage from the extension

Notes

  • A named transaction (store.txn(fn, name)) arrives as a single action.
  • Default (non-enumerable) stores seed lazily as state first changes; only store(id, { enumerable: true }) is seeded up front.
  • Only how you obtain the Store differs per framework (useStore, injectStore, getValdresContext, or the bare instance).