{JSON.stringify({ accel, rot }, null, 2)}
{profile.email}
{profile.bio}
{{ profile.email }}
{{ profile.bio }}
{profile.value.email}
{profile.value.bio}
{profile().email}
{profile().bio}
{{ profile().email }}
{{ profile().bio }}
} `, }) export class ProfileView { profile = injectValue(profileAtom) store = injectStore() editing = signal(false) } @Component({ selector: "profile-edit-form", providers: [provideValdresScope({ scopeId: "edit-profile" })], outputs: ["close"], template: `Count: {count}
Doubled: {doubled}
{JSON.stringify({ name, email, message }, null, 2)}
}
```
## Subscribing to a family
Unlike other libraries, you can subscribe to an entire `atomFamily` to get notified when items are added or removed.
```ts
import { atomFamily, store } from "valdres"
const userAtom = atomFamily()
const myStore = store()
// Family callbacks receive the arguments of the member that changed
myStore.sub(userAtom, id => {
console.log("Changed user ID:", id)
})
myStore.set(userAtom("user-1"), { name: "Alice" })
// logs: Changed user ID: user-1
myStore.set(userAtom("user-2"), { name: "Bob" })
// logs: Changed user ID: user-2
```
## State outside of components
Valdres state lives in a store, not in components. You can read and write state from anywhere — event handlers, WebSocket callbacks, service workers, or tests.
```ts
import { atom, store } from "valdres"
const myStore = store()
const statusAtom = atom("idle")
// From a WebSocket
ws.addEventListener("message", event => {
const data = JSON.parse(event.data)
myStore.set(statusAtom, data.status)
})
// From a timer
setInterval(() => {
myStore.set(statusAtom, "polling")
}, 5000)
// In a test
myStore.set(statusAtom, "testing")
expect(myStore.get(statusAtom)).toBe("testing")
```
---
Source: https://valdres.dev/guides/performance.md
# Performance
> **Continuously verified**
>
>
> These benchmarks run on every commit. If a regression is detected, the PR is blocked until performance is restored.
Benchmarks: [https://valdres.dev/guides/performance](https://valdres.dev/guides/performance)
## Why the core is fast
The engine is optimized for minimal allocations and cache-friendly data structures — WeakMap-based storage with no intermediate objects, and transactions that batch updates so subscribers fire once.
In **React** specifically, the adapter is built on `useSyncExternalStore` (where Jotai uses `useEffect` + `useReducer`), which avoids extra renders and effect-cleanup overhead. The other adapters integrate with each framework's native reactivity — Vue refs, Svelte runes, Solid signals, Angular signals — so there the relevant performance is the framework's own.
> **What these numbers are (and aren't)**
>
>
> Every benchmark here compares the core Valdres engine against Jotai at the JavaScript level. They are not framework-rendering benchmarks, and Valdres does not claim to be faster than a framework's native reactivity (e.g. Svelte runes). The adapters exist to share one store across frameworks, not to outrun them.
## Running benchmarks locally
```bash
# Bun (JSC/Safari engine)
bun run --cwd packages/valdres test:bench
# Node.js (V8/Chrome engine)
bun run --cwd packages/valdres test:bench:node
```
## Historical trends
Performance is tracked over time on every push to `main`. View the live, always-current numbers and history at [bencher.dev/perf/valdres](https://bencher.dev/perf/valdres).
---
Source: https://valdres.dev/guides/quick-start-angular.md
# Quick Start with Angular
## Install
```bash
npm install valdres valdres-angular
```
## Create your first atom
```ts
// store.ts
import { atom } from "valdres"
export const countAtom = atom(0)
```
## Use it in a component
```ts
import { Component } from "@angular/core"
import { injectAtom } from "valdres-angular"
import { countAtom } from "./store"
@Component({
template: `
Count: {{ count() }}
The count is {{ count() }}
`, }) export class DisplayComponent { count = injectValue(countAtom) } ``` ## Next steps - [Core Concepts](https://valdres.dev/guides/core-concepts) — atoms, selectors, families, stores - [Patterns & Recipes](https://valdres.dev/guides/patterns) — real-world examples --- Source: https://valdres.dev/guides/quick-start-solid.md # Quick Start with Solid ## Install ```bash npm install valdres valdres-solid ``` ## Create your first atom ```ts // store.ts import { atom } from "valdres" export const countAtom = atom(0) ``` ## Use it in a component ```tsx import { createAtom } from "valdres-solid" import { countAtom } from "./store" function Counter() { const [count, setCount] = createAtom(countAtom) return (Count: {count()}
The count is {count()}
} ``` ## Next steps - [Core Concepts](https://valdres.dev/guides/core-concepts) — atoms, selectors, families, stores - [Patterns & Recipes](https://valdres.dev/guides/patterns) — real-world examples --- Source: https://valdres.dev/guides/quick-start-svelte.md # Quick Start with Svelte `valdres-svelte` is the Svelte 5 (runes) adapter. It bridges valdres atoms and selectors into reactive boxes you read with `.current`, plus a store-contract bridge for `$`-syntax and a provider tier for SSR. ## Install ```bash npm install valdres valdres-svelte ``` ## Create your first atom ```ts // store.ts import { atom } from "valdres" export const countAtom = atom(0) ``` ## Provide a store Call `setValdresContext` once near the root so descendants share a store. With no argument it creates a `store({ batchUpdates: true })` for the component tree — which on the server means one store per request, the pattern SvelteKit wants. ```svelte {@render children()} ``` ## Read and write with `fromState` `fromState` returns a reactive box. For an atom, `.current` is readable **and** writable, so `bind:value` and `count.current++` just work. For a read-modify-write it also has `update(fn)` (like `svelte/store`'s `Writable.update`) and a `reset()`. ```svelteCount: {count.current}
``` Pass an explicit store as the second argument (`fromState(countAtom, store)`) when you're outside component initialization — e.g. in plain `.svelte.ts` module state. Without it, the box resolves the store from context, which is only available during init. A selector (or any read-only state) yields a box with a read-only `.current`: ```svelteDoubled: {double.current}
``` ## `$`-syntax with `toStore` For Svelte's store contract (`$`-prefix auto-subscription, `bind:value={$count$}`), use `toStore`. An atom becomes a `Writable`; a selector becomes a read-only `Readable`. The store argument is optional and falls back to context. ```svelteCount: {$count$}
``` ## Scoped state `scope` creates a child store layered over the parent for a subtree, with an optional `initialize` to seed it. It detaches automatically when the component is destroyed. Descendants reading via `fromState`/`toStore` resolve the scoped store. ```svelteScoped count: {count.current}
``` ## Transactions `transaction()` returns a runner bound to the context store, captured at component init. That capture is what makes it safe to call from an event handler — Svelte throws `lifecycle_outside_component` if you call `getContext` inside a handler. ```svelte ``` ## Async selectors A selector can return a promise. Core erases asyncness, so `fromState(sel).current` is typed `V | PromiseLoading…
{:then u}Hello {u.name}
{:catch error}Failed: {error.message}
{/await} ``` When you'd rather branch on flags than `await`, `resourceState` runs the promise detection for you and returns `{ current, loading, error }`: ```svelte {#if user.loading}Loading…
{:else if user.error}Failed: {String(user.error)}
{:else}Hello {user.current?.name}
{/if} ``` ## SvelteKit (SSR) Load data on the server, then map it to atoms with `initialize` in the root layout. Because `initialize` runs on both the server render and the client hydration, the atoms hold the right values in both passes — no custom serializer needed. ```ts // +layout.server.ts export const load = async () => { return { count: 7 } // plain, serializable } ``` ```svelte {@render children()} ``` If your `load` data is itself a `dehydrate(store)` payload (e.g. produced by an API that ran a server-side valdres store), pass it as `hydrate` instead — it's applied as a standalone commit on the fresh store. Atoms carrying a codec schema round-trip `BigInt`/`Date`/`Map`/`Set` over plain JSON automatically. When both are given, `initialize` runs first so hydrated values win. ```svelte {@render children()} ``` ## Gotcha: lazy bootstrap The reactive box subscribes lazily (it's built on Svelte's `createSubscriber`, the same primitive behind `MediaQuery`). A valdres atom with an `onMount` bootstrap — the `@valdres/browser-*` packages, for instance — only starts once `.current` is **read inside an effect** (a template expression, `$derived`, `$effect`). A component that reads the value solely from an event handler will see the unbootstrapped default. Read it in the template, or via `$`-syntax with `toStore`, to start the subscription. If you genuinely need it to start without rendering it, force the subscription with a throwaway effect: ```svelte ``` ## Next steps - [Core Concepts](https://valdres.dev/guides/core-concepts) — atoms, selectors, families, stores - [Patterns & Recipes](https://valdres.dev/guides/patterns) — real-world examples --- Source: https://valdres.dev/guides/quick-start-vue.md # Quick Start with Vue ## Install ```bash npm install valdres valdres-vue ``` ## Create your first atom ```ts // store.ts import { atom } from "valdres" export const countAtom = atom(0) ``` ## Use it in a component ```vueCount: {{ count }}
The count is {{ count }}
``` ## Next steps - [Core Concepts](https://valdres.dev/guides/core-concepts) — atoms, selectors, families, stores - [Patterns & Recipes](https://valdres.dev/guides/patterns) — real-world examples --- Source: https://valdres.dev/guides/schema-validation.md # Schema Validation Atoms and selectors accept an optional `schema` that validates values at runtime — and types your state without a generic. Validation is **opt-in per store** and off by default, so it adds zero cost unless you turn it on. ```ts import { atom, store } from "valdres" import { z } from "zod" const userAtom = atom( { name: "Ada", age: 36 }, { name: "userAtom", schema: z.object({ name: z.string(), age: z.number().min(0) }), }, ) const s = store({ schemaValidation: true }) s.set(userAtom, { name: "Bob", age: -1 }) // SchemaValidationError: Schema validation failed for 'userAtom': … ``` ## Two features in one 1. **Type inference.** The schema is the single source of truth for the atom's type — `atom(undefined, { schema: z.string() })` is `AtomCount: {count.current}
``` The second `store` argument defaults to the store from [setValdresContext](https://valdres.dev/svelte/setValdresContext) / [scope](https://valdres.dev/svelte/scope), resolved via [getValdresContext](https://valdres.dev/svelte/getValdresContext). Because context is only available during component initialization, pass an explicit `store` to use the box in plain `.svelte.ts` module state or during SSR. ## Returns For an **atom** (`FromStateAtomHello {u.name}
{/await} ``` > **Lazy bootstrap** > > > The box is built on Svelte's `createSubscriber` (the primitive behind `MediaQuery`), so the underlying subscription is lazy: it starts on the first effect that reads `.current`. A valdres `onMount`-driven atom — the [@valdres/browser-\*](https://valdres.dev/svelte/plugins/browser-online) packages — therefore bootstraps only once `.current` is read in the template or an effect. If a component reads the value solely from an event handler, start the subscription with a throwaway effect: `$effect(() => void box.current)`. ## See also - [toStore](https://valdres.dev/svelte/toStore) — Svelte store-contract bridge for `$`-syntax - [resourceState](https://valdres.dev/svelte/resourceState) — async selectors with `loading` / `error` - [setValdresContext](https://valdres.dev/svelte/setValdresContext) — provide a store to the component tree --- Source: https://valdres.dev/svelte/getValdresContext.md # getValdresContext Signature `getValdresContext(): Store` valdres-svelte Read the current store from context Returns the store set by the nearest [setValdresContext](https://valdres.dev/svelte/setValdresContext) / [scope](https://valdres.dev/svelte/scope) ancestor. Most of the time you don't need it — [fromState](https://valdres.dev/svelte/fromState), [toStore](https://valdres.dev/svelte/toStore), and [transaction](https://valdres.dev/svelte/transaction) resolve the context store on their own. Reach for it when you need direct `store.get` / `store.set` access. ## Usage ```svelte ``` > **Call during initialization** > > > Svelte throws `lifecycle_outside_component` for `getContext` outside component initialization, so capture the store at the top level rather than inside an event handler. To run writes from a handler, use [transaction](https://valdres.dev/svelte/transaction), which captures the store for you. ## See also - [setValdresContext](https://valdres.dev/svelte/setValdresContext) — provide the store - [transaction](https://valdres.dev/svelte/transaction) — handler-safe transaction runner --- Source: https://valdres.dev/svelte/resourceState.md # resourceState Signature `resourceStateLoading…
{:else if user.error}Failed: {String(user.error)}
{:else}Hello {user.current?.name}
{/if} ``` When you'd rather `await` than branch on flags, [fromState](https://valdres.dev/svelte/fromState) plus Svelte's `{#await}` block works directly on the `T | PromiseHello {u.name}
{/await} ``` ## Returns | Member | Type | Description | | --------- | ---------------- | ------------------------------------------------------- | | `current` | `T \| undefined` | Resolved value, or `undefined` while pending or errored | | `loading` | `boolean` | `true` while the selector's promise is pending | | `error` | `unknown` | The rejection reason, if the promise rejected | ## See also - [fromState](https://valdres.dev/svelte/fromState) — reactive box; `{#await}` works on its `.current` - [toStore](https://valdres.dev/svelte/toStore) — Svelte store-contract bridge --- Source: https://valdres.dev/svelte/scope.md # scope Signature `scope(scopeId?: string, options?: ScopeOptions): Store` valdres-svelte Scoped child store for a component subtree Creates a scoped child store layered over the parent for a subtree, and exposes it to descendants via context. Reuses an existing scope of the same `scopeId` if one is already in the tree; otherwise it creates one and detaches it automatically when the owning component is destroyed. Descendants reading via [fromState](https://valdres.dev/svelte/fromState) / [toStore](https://valdres.dev/svelte/toStore) resolve the scoped store. ## Usage ```svelteScoped count: {count.current}
``` The optional `initialize` callback seeds the scoped store inside a transaction when the scope is created — the same `InitializeCallback` contract used by [setValdresContext](https://valdres.dev/svelte/setValdresContext). It must be called during component initialization (it reads context and registers an `onDestroy` cleanup). ## See also - [setValdresContext](https://valdres.dev/svelte/setValdresContext) — provide the root store - [getValdresContext](https://valdres.dev/svelte/getValdresContext) — read the current (scoped) store --- Source: https://valdres.dev/svelte/setValdresContext.md # setValdresContext Signature `setValdresContext(storeOrOptions?: Store | SetValdresContextOptions): Store` valdres-svelte Provide a store to the component tree Creates (or adopts) a valdres store and exposes it to descendants via Svelte context — the root of the adapter's provider tier. Call it once near the root. With no argument it creates `store({ batchUpdates: true })` for the component tree, which on the server means one store per request — the canonical SvelteKit pattern that avoids a module-level shared-store leak. Returns the store now in context. ## Usage ```svelte {@render children()} ``` Descendants then read state with [fromState](https://valdres.dev/svelte/fromState) / [toStore](https://valdres.dev/svelte/toStore) and no explicit store argument. ## Options Pass an options object to seed the store: | Option | Type | Description | | ---------------- | -------------------- | -------------------------------------------------------------------------------------------- | | `store` | `Store` | Use this store instead of auto-creating one (warns if not created with `batchUpdates: true`) | | `initialize` | `InitializeCallback` | Seed the store inside a transaction before it's exposed | | `hydrate` | `DehydratedState` | Apply a `dehydrate(store)` payload to the fresh store | | `hydrateOptions` | `HydrateOptions` | Forwarded to core `hydrate` (`{ invalid: "throw" \| "skip" }`) | When both `initialize` and `hydrate` are given, `initialize` runs first so transferred values win. ## SvelteKit (SSR) Load data on the server, then map it to atoms with `initialize`. Because `initialize` runs on both the server render and client hydration, the atoms hold the right values in both passes — no custom serializer needed. ```ts // +layout.server.ts export const load = async () => { return { count: 7 } // plain, serializable } ``` ```svelte {@render children()} ``` If your `load` data is itself a `dehydrate(store)` payload (e.g. from an API that ran a server-side store), pass it as `hydrate` instead. Atoms carrying a codec schema round-trip `BigInt` / `Date` / `Map` / `Set` over plain JSON automatically. ## See also - [getValdresContext](https://valdres.dev/svelte/getValdresContext) — read the store from context - [scope](https://valdres.dev/svelte/scope) — scoped child store for a subtree --- Source: https://valdres.dev/svelte/toStore.md # toStore Signature `toStoreCount: {$count$}
``` The `store` argument is optional and defaults to the context store (via [getValdresContext](https://valdres.dev/svelte/getValdresContext)), so it can be omitted during component initialization — consistent with every other primitive. ## See also - [fromState](https://valdres.dev/svelte/fromState) — rune-based reactive box (preferred) - [setValdresContext](https://valdres.dev/svelte/setValdresContext) — provide a store to the component tree --- Source: https://valdres.dev/svelte/transaction.md # transaction Signature `transaction(store?: Store): (callback: TransactionFn, name?: string) => void` valdres-svelte Batch multiple writes into a single commit Returns a transaction runner bound to the current context store, captured at component initialization. That capture is what makes it safe to call from an event handler — Svelte throws `lifecycle_outside_component` if you call `getContext` inside a handler. Writes inside the callback commit together, so subscribers and selectors observe one atomic update. Forwards core's optional devtools `name`. Callbacks must be synchronous. Returning a Promise or other thenable throws without automatically committing staged writes; await asynchronous work before calling the transaction runner. ## Usage ```svelte ``` Pass an explicit `store` to bind the runner to a specific store instead of the context store. ## See also - [setValdresContext](https://valdres.dev/svelte/setValdresContext) — provide the store the runner binds to - [scope](https://valdres.dev/svelte/scope) — scoped child store --- Source: https://valdres.dev/vue/createValdres.md # createValdres Signature `createValdres(options?: ValdresPluginOptions): Plugin` valdres-vue Provide a store to your Vue app Creates a Vue plugin that provides a valdres store to all components via Vue's provide/inject system. ## Usage ```ts import { createApp } from "vue" import { createValdres } from "valdres-vue" import App from "./App.vue" const app = createApp(App) app.use(createValdres()) app.mount("#app") ``` ## With a custom store ```ts import { store } from "valdres" import { createValdres } from "valdres-vue" const myStore = store() app.use(createValdres({ store: myStore })) ``` ## See also - [useStore](https://valdres.dev/vue/useStore) — access the store in components - [store](https://valdres.dev/valdres/store) — create a store instance --- Source: https://valdres.dev/vue/useAtom.md # useAtom Signature `useAtom