### Install Dependencies with Bun or NPM Source: https://github.com/legendapp/legend-state/blob/main/AGENTS.md Use `bun install` for dependency management, falling back to `npm install` if necessary. ```bash bun install or npm install ``` -------------------------------- ### Observable State Management in React Source: https://github.com/legendapp/legend-state/blob/main/README.md Demonstrates basic observable creation, getting and setting values, computed observables, and observing changes. Use this for managing global or component-level state in React applications. ```jsx import { observable, observe } from "@legendapp/state" import { observer } from "@legendapp/state/react" const settings$ = observable({ theme: 'dark' }) // get returns the raw data settings$.theme.get() // 'dark' // set sets settings$.theme.set('light') // Computed observables with just a function const isDark$ = observable(() => settings$.theme.get() === 'dark') // observing contexts re-run when tracked observables change observe(() => { console.log(settings$.theme.get()) }) const Component = observer(function Component() { const theme = state$.settings.theme.get() return
Theme: {theme}
}) ``` -------------------------------- ### Create pre-configured sync factories with configureSynced Source: https://context7.com/legendapp/legend-state/llms.txt Use `configureSynced` to create a version of `synced` with default options baked in, useful for sharing configurations. This example creates a project-wide fetch synced with auth headers and local persistence. ```typescript import { configureSynced } from '@legendapp/state/sync'; import { syncedFetch } from '@legendapp/state/sync-plugins/fetch'; import { ObservablePersistLocalStorage } from '@legendapp/state/persist-plugins/local-storage'; // Create a project-wide fetch synced with auth headers and local persist const mySyncedFetch = configureSynced(syncedFetch, { getInit: { headers: { Authorization: `Bearer ${getToken()}` } }, persist: { plugin: ObservablePersistLocalStorage }, retry: { infinite: true }, }); const posts$ = observable(mySyncedFetch({ get: '/api/posts', set: '/api/posts' })); const comments$ = observable(mySyncedFetch({ get: '/api/comments' })); ``` -------------------------------- ### HTTP fetch sync plugin with syncedFetch Source: https://context7.com/legendapp/legend-state/llms.txt The `syncedFetch` plugin syncs an observable with a REST endpoint. It supports GET and SET URLs, custom request init, response type, and an `onSaved` callback. Reading the observable triggers the GET fetch, and setting a value triggers the SET fetch after debouncing. ```typescript import { observable } from '@legendapp/state'; import { syncedFetch } from '@legendapp/state/sync-plugins/fetch'; const article$ = observable( syncedFetch<{ id: number; title: string; body: string }>({ get: 'https://jsonplaceholder.typicode.com/posts/1', set: 'https://jsonplaceholder.typicode.com/posts/1', setInit: { method: 'PUT', headers: { 'Content-Type': 'application/json' } }, valueType: 'json', onSaved: ({ saved, input }) => { console.log('Server confirmed save:', input.id); return saved; // merge saved response back in }, persist: { name: 'article-1' }, }), ); // Reading triggers the GET fetch console.log(article$.title.get()); // undefined → then 'sunt aut facere...' article$.title.set('My new title'); // triggers the PUT fetch after 0ms debounce ``` -------------------------------- ### Wait for Conditions with when() and whenReady() Source: https://context7.com/legendapp/legend-state/llms.txt Use `when` to get a Promise that resolves when a predicate is truthy, optionally running an effect. `whenReady` additionally ensures objects/arrays are non-empty. Both support array predicates. ```typescript import { observable, when, whenReady } from '@legendapp/state'; const auth$ = observable({ isLoaded: false, user: null as any }); // As a Promise await when(() => auth$.isLoaded.get()); console.log('Auth is loaded'); // With an effect callback when( () => auth$.user.get(), (user) => console.log('Logged in as', user.name), ); // Array predicates — waits for ALL to be truthy const db$ = observable({ connected: false }); await when([() => auth$.isLoaded.get(), () => db$.connected.get()]); console.log('Both ready'); // whenReady — empty objects/arrays don't count as ready const items$ = observable([]); await whenReady(items$); console.log('Items array is non-empty:', items$.get()); ``` -------------------------------- ### Access sync/persist state metadata with syncState Source: https://context7.com/legendapp/legend-state/llms.txt Use `syncState` to get an observable with metadata about the sync/persist lifecycle. It also exposes `sync()` and `reset()` methods for manual control. ```typescript import { observable } from '@legendapp/state'; import { syncState } from '@legendapp/state'; import { synced } from '@legendapp/state/sync'; const todos$ = observable(synced({ get: async () => fetch('/api/todos').then(r => r.json()), set: async ({ value }) => fetch('/api/todos', { method: 'PUT', body: JSON.stringify(value) }), persist: { name: 'todos' }, })); const state$ = syncState(todos$); console.log(state$.isLoaded.get()); // false initially console.log(state$.numPendingGets.get()); // 1 while fetching // Manually trigger sync await state$.sync(); // Reset persist and re-fetch await state$.reset(); ``` -------------------------------- ### syncedFetch(props) Source: https://context7.com/legendapp/legend-state/llms.txt An HTTP fetch sync plugin that syncs an observable with a REST endpoint. It supports GET and SET URLs, custom request init, response type, and an `onSaved` callback for processing the server response. ```APIDOC ## syncedFetch(props) ### Description HTTP fetch sync plugin. Syncs an observable with a REST endpoint. Supports GET and SET URLs, custom request init, response type, and an `onSaved` callback for processing the server response. ### Parameters #### Request Body - **props** (object) - Required - Configuration properties for the fetch sync. - **get** (string) - Required - The URL for GET requests. - **set** (string) - Required - The URL for SET requests. - **setInit** (object) - Optional - Request init options for SET requests. - **method** (string) - HTTP method (e.g., 'PUT'). - **headers** (object) - Request headers. - **valueType** (string) - Optional - The expected response type ('json', 'text', etc.). Defaults to 'json'. - **onSaved** (function) - Optional - Callback function executed after a successful save. - **saved** - The data saved by the server. - **input** - The input data sent to the server. - **persist** (object) - Optional - Configuration for local persistence. - **name** (string) - Required - The name for the persistence key. ### Request Example ```ts import { observable } from '@legendapp/state'; import { syncedFetch } from '@legendapp/state/sync-plugins/fetch'; const article$ = observable( syncedFetch<{ id: number; title: string; body: string(>({ get: 'https://jsonplaceholder.typicode.com/posts/1', set: 'https://jsonplaceholder.typicode.com/posts/1', setInit: { method: 'PUT', headers: { 'Content-Type': 'application/json' } }, valueType: 'json', onSaved: ({ saved, input }) => { console.log('Server confirmed save:', input.id); return saved; // merge saved response back in }, persist: { name: 'article-1' }, }), ); // Reading triggers the GET fetch console.log(article$.title.get()); // undefined → then 'sunt aut facere...' article$.title.set('My new title'); // triggers the PUT fetch after 0ms debounce ``` ``` -------------------------------- ### Low-Level Bidirectional Link with linked() Source: https://context7.com/legendapp/legend-state/llms.txt Use `linked` to create a primitive for custom observable data sources with optional `get` and `set` functions, supporting async operations and subscriptions. This is the basis for `computed` and `synced`. ```typescript import { observable, linked } from '@legendapp/state'; // Two-way link to localStorage const theme$ = observable( linked({ get: () => localStorage.getItem('theme') ?? 'dark', set: ({ value }) => localStorage.setItem('theme', value), }), ); theme$.set('light'); console.log(localStorage.getItem('theme')); // 'light' console.log(theme$.get()); // 'light' ``` -------------------------------- ### Undo/Redo History with `undoRedo` Helper Source: https://context7.com/legendapp/legend-state/llms.txt The `undoRedo` helper attaches undo/redo capability to any observable. It returns functions to perform undo/redo actions, observables for available counts, and a method to get the history. It does not record remote or persisted changes. ```ts import { observable } from '@legendapp/state'; import { undoRedo } from '@legendapp/state/helpers/undoRedo'; const doc$ = observable({ title: 'Hello', body: 'World' }); const { undo, redo, undos$, redos$, getHistory } = undoRedo(doc$, { limit: 50 }); doc$.title.set('Hi'); doc$.body.set('Everyone'); console.log(undos$.get()); // 2 console.log(redos$.get()); // 0 undo(); console.log(doc$.title.get()); // 'Hi' — body reverted console.log(undos$.get()); // 1 redo(); console.log(doc$.body.get()); // 'Everyone' — redone ``` -------------------------------- ### `linked(options)` Source: https://context7.com/legendapp/legend-state/llms.txt Low-level bidirectional link primitive. The building block used by `computed` and `synced`. Creates a linked observable that resolves via custom `get` and optionally `set` functions, supporting any async or subscription-based data source. ```APIDOC ## `linked(options)` — Low-level bidirectional link primitive The building block used by `computed` and `synced`. Creates a linked observable that resolves via custom `get` and optionally `set` functions, supporting any async or subscription-based data source. ```ts import { observable, linked } from '@legendapp/state'; // Two-way link to localStorage const theme$ = observable( linked({ get: () => localStorage.getItem('theme') ?? 'dark', set: ({ value }) => localStorage.setItem('theme', value), }), ); theme$.set('light'); console.log(localStorage.getItem('theme')); // 'light' console.log(theme$.get()); // 'light' ``` ``` -------------------------------- ### Build Project Including Checks and Bundling Source: https://github.com/legendapp/legend-state/blob/main/AGENTS.md Perform a full build process that includes linting, formatting checks, tests, and bundling with tsup. ```bash npm run build ``` -------------------------------- ### Run Unit Tests with Bun or Jest Source: https://github.com/legendapp/legend-state/blob/main/AGENTS.md Execute the local unit test suite using Bun. `npm test` is available for CI parity with Jest. ```bash bun test npm test ``` -------------------------------- ### Run Post-build Script Source: https://github.com/legendapp/legend-state/blob/main/CLAUDE.md Execute a post-build script after the tsup bundling process. This script performs actions necessary after the main build artifacts are generated. ```bash bun run posttsup.ts ``` -------------------------------- ### Run Tests with Timeout for Quick Feedback Source: https://github.com/legendapp/legend-state/blob/main/AGENTS.md Execute tests using Bun with a specified timeout, useful for rapid local feedback during development. ```bash bun test --timeout 50 ``` -------------------------------- ### Synced and Persistent State Management Source: https://github.com/legendapp/legend-state/blob/main/README.md Shows how to configure observable state with persistence and synchronization using `syncedKeel`. This is useful for building local-first applications that require optimistic updates, retries, and minimal diff syncing. ```js const state$ = observable({ users: syncedKeel({ list: queries.getUsers, create: mutations.createUsers, update: mutations.updateUsers, delete: mutations.deleteUsers, persist: { name: 'users', retrySync: true }, debounceSet: 500, retry: { infinite: true, }, changesSince: 'last-sync', }), // direct link to my user within the users observable me: () => state$.users['myuid'] }) observe(() => { // get() activates through to state$.users and starts syncing. // it updates itself and re-runs observers when name changes const name = me$.name.get() }) // Setting a value goes through to state$.users and saves update to server me$.name.set('Annyong') ``` -------------------------------- ### computed(get, set?) Source: https://context7.com/legendapp/legend-state/llms.txt Creates a derived observable whose value is computed from other observables. It can be read-only or two-way. Computations run lazily and only when the computed value is observed and its dependencies change. Supports asynchronous computations from Promises. ```APIDOC ## computed(get, set?) — Derived observable Creates a read-only (or two-way) computed observable whose value is derived from other observables. The computation re-runs lazily — only when the computed is observed and a dependency changes. ```ts import { observable, computed } from '@legendapp/state'; const store$ = observable({ firstName: 'John', lastName: 'Doe', price: 10, qty: 3 }); // Read-only computed const fullName$ = computed(() => `${store$.firstName.get()} ${store$.lastName.get()}`); console.log(fullName$.get()); // 'John Doe' store$.firstName.set('Jane'); console.log(fullName$.get()); // 'Jane Doe' // Two-way computed (writable derived value) const total$ = computed( () => store$.price.get() * store$.qty.get(), (value) => store$.price.set(value / store$.qty.get()), ); console.log(total$.get()); // 30 total$.set(60); // sets price to 20 console.log(store$.price.get()); // 20 // Async computed from a Promise const user$ = computed(async () => { const res = await fetch('/api/me'); return res.json(); }); // user$.get() is undefined until the promise resolves ``` ``` -------------------------------- ### Fine-grained Reactivity with Memo Source: https://github.com/legendapp/legend-state/blob/main/README.md Illustrates how to use `useObservable` and `Memo` for fine-grained reactivity in React. This approach ensures that only the necessary parts of the UI update, improving performance by minimizing component re-renders. ```jsx function FineGrained() { const count$ = useObservable(0) useInterval(() => { count$.set(v => v + 1) }, 600) // The text updates itself so the component doesn't re-render return (
Count: {count$}
) } ``` -------------------------------- ### Run Jest Test Suite Source: https://github.com/legendapp/legend-state/blob/main/CLAUDE.md Execute the Jest test suite for the project. This is the standard command for running tests. ```bash npm test ``` ```bash jest ``` -------------------------------- ### Build with tsup Bundler Source: https://github.com/legendapp/legend-state/blob/main/CLAUDE.md Build project artifacts using the tsup bundler to generate distribution files. This command is part of the build process. ```bash bunx tsup ``` -------------------------------- ### `event()` Source: https://context7.com/legendapp/legend-state/llms.txt Creates a lightweight event emitter built on an observable number counter. Use `.fire()` to dispatch, `.on(cb)` to subscribe, and pass it to `when` or `observe` to react to events. ```APIDOC ## `event()` — Observable event bus Creates a lightweight event emitter built on an observable number counter. Use `.fire()` to dispatch, `.on(cb)` to subscribe, and pass it to `when` or `observe` to react to events. ```ts import { event, observe, when } from '@legendapp/state'; const submitEvent = event(); // Subscribe with .on (returns a dispose function) const dispose = submitEvent.on(() => console.log('Submitted!')); submitEvent.fire(); // logs: Submitted! // React inside observe observe(() => { submitEvent.get(); // tracks the event console.log('Event fired, count:', submitEvent.get()); }); // Wait for event with `when` when(submitEvent, () => console.log('First fire happened')); submitEvent.fire(); dispose(); // unsubscribe ``` ``` -------------------------------- ### Check Bundle Size Source: https://github.com/legendapp/legend-state/blob/main/AGENTS.md Monitor the bundle footprint of the generated artifacts before a release or significant change. ```bash npm run checksize ``` -------------------------------- ### Verify Code Formatting with Prettier Source: https://github.com/legendapp/legend-state/blob/main/AGENTS.md Check if the code adheres to Prettier formatting standards. Append `:write` to auto-fix. ```bash npm run format:check # or npm run format:check:write ``` -------------------------------- ### configureSynced(fnOrOptions, defaults?) Source: https://context7.com/legendapp/legend-state/llms.txt Creates pre-configured sync factories by applying default options to a sync function or options object. This is useful for sharing a base configuration across multiple observables. ```APIDOC ## configureSynced(fnOrOptions, defaults?) ### Description Create pre-configured sync factories. Creates a version of `synced` (or any sync plugin) with default options baked in, useful for sharing a base configuration across many observables. ### Parameters #### Request Body - **fnOrOptions** (function | object) - Required - The sync function or options object to configure. - **defaults** (object) - Optional - Default options to apply. ### Request Example ```ts import { configureSynced } from '@legendapp/state/sync'; import { syncedFetch } from '@legendapp/state/sync-plugins/fetch'; import { ObservablePersistLocalStorage } from '@legendapp/state/persist-plugins/local-storage'; // Create a project-wide fetch synced with auth headers and local persist const mySyncedFetch = configureSynced(syncedFetch, { getInit: { headers: { Authorization: `Bearer ${getToken()}` } }, persist: { plugin: ObservablePersistLocalStorage }, retry: { infinite: true }, }); const posts$ = observable(mySyncedFetch({ get: '/api/posts', set: '/api/posts' })); const comments$ = observable(mySyncedFetch({ get: '/api/comments' })); ``` ``` -------------------------------- ### `when(predicate, effect?)` / `whenReady(predicate, effect?)` Source: https://context7.com/legendapp/legend-state/llms.txt Returns a Promise that resolves once the predicate becomes truthy. Optionally runs an `effect` when it resolves. `whenReady` also requires that objects/arrays are non-empty. Both accept an array of predicates. ```APIDOC ## `when(predicate, effect?)` / `whenReady(predicate, effect?)` — Wait for a condition Returns a Promise that resolves once the predicate becomes truthy. Optionally runs an `effect` when it resolves. `whenReady` also requires that objects/arrays are non-empty. Both accept an array of predicates. ```ts import { observable, when, whenReady } from '@legendapp/state'; const auth$ = observable({ isLoaded: false, user: null as any }); // As a Promise await when(() => auth$.isLoaded.get()); console.log('Auth is loaded'); // With an effect callback when( () => auth$.user.get(), (user) => console.log('Logged in as', user.name), ); // Array predicates — waits for ALL to be truthy const db$ = observable({ connected: false }); await when([() => auth$.isLoaded.get(), () => db$.connected.get()]); console.log('Both ready'); // whenReady — empty objects/arrays don't count as ready const items$ = observable([]); await whenReady(items$); console.log('Items array is non-empty:', items$.get()); ``` ``` -------------------------------- ### `observe(selector, reaction?, options?)` Source: https://context7.com/legendapp/legend-state/llms.txt Runs a function immediately and re-runs it whenever any observable accessed inside changes. Returns a dispose function. Supports a separate reaction callback for responding only to changes. ```APIDOC ## `observe(selector, reaction?, options?)` — Reactive side-effect runner Runs a function immediately and re-runs it whenever any observable accessed inside changes. Returns a dispose function. Supports a separate reaction callback for responding only to changes (not the initial run). ```ts import { observable, observe } from '@legendapp/state'; const pos$ = observable({ x: 0, y: 0 }); // Runs immediately, re-runs whenever pos$.x or pos$.y change const dispose = observe(() => { console.log('Position:', pos$.x.get(), pos$.y.get()); }); // logs: Position: 0 0 pos$.x.set(5); // logs: Position: 5 0 // Cleanup / stop observing dispose(); // Two-argument form: selector + reaction (reaction only fires on changes, not initial run) const counter$ = observable(0); observe( () => counter$.get(), (e) => { console.log(`Changed from ${e.previous} to ${e.value}`); if (e.value > 10) e.cancel = true; // stop observing }, ); // Cleanup with e.onCleanup observe((e) => { const id = setInterval(() => console.log(counter$.get()), 1000); e.onCleanup = () => clearInterval(id); }); ``` ``` -------------------------------- ### Observable Event Bus with event() Source: https://context7.com/legendapp/legend-state/llms.txt Create an event emitter using `event()`. Use `.fire()` to dispatch, `.on(cb)` to subscribe (returning a dispose function), and integrate with `observe` or `when` to react to events. ```typescript import { event, observe, when } from '@legendapp/state'; const submitEvent = event(); // Subscribe with .on (returns a dispose function) const dispose = submitEvent.on(() => console.log('Submitted!')); submitEvent.fire(); // logs: Submitted! // React inside observe observe(() => { submitEvent.get(); // tracks the event console.log('Event fired, count:', submitEvent.get()); }); // Wait for event with `when` when(submitEvent, () => console.log('First fire happened')); submitEvent.fire(); dispose(); // unsubscribe ``` -------------------------------- ### Running CRUD Tests Source: https://github.com/legendapp/legend-state/blob/main/plans/1-fix-crud-retry.md Commands to execute the CRUD and Keel test suites, along with type checking, to validate the implementation of the CRUD create retry fix. ```bash bun test tests/crud.test.ts --timeout 200 bun test tests/keel.test.ts --timeout 200 npm run typecheck ``` -------------------------------- ### useObserve Source: https://context7.com/legendapp/legend-state/llms.txt A React hook version of `observe`. It sets up a reactive observation that automatically disposes when the component unmounts. It can take a selector function and an optional reaction function and options. ```APIDOC ## `useObserve(selector, reaction?, options?)` — React hook for `observe` A React hook version of `observe`. Sets up a reactive observation that auto-disposes when the component unmounts. ```tsx import { observable } from '@legendapp/state'; import { observer, useObserve } from '@legendapp/state/react'; const socket$ = observable({ connected: false, lastMessage: '' }); const ChatMonitor = observer(function ChatMonitor() { // Runs immediately and re-runs when socket$.connected changes useObserve( () => socket$.connected.get(), ({ value, previous }) => { if (value && !previous) console.log('Socket reconnected'); }, ); // Observe with cleanup useObserve((e) => { const id = socket$.lastMessage.get(); // track document.title = `New message: ${id}`; e.onCleanup = () => { document.title = 'App'; }; }); return
Connected: {socket$.connected.get() ? 'Yes' : 'No'}
; }); ``` ``` -------------------------------- ### Check Core Bundle Size Source: https://github.com/legendapp/legend-state/blob/main/CLAUDE.md Check the gzipped bundle size specifically for the core Legend-State package. Useful for isolating performance concerns. ```bash npm run checksize:core ``` -------------------------------- ### Check Sync Package Bundle Size Source: https://github.com/legendapp/legend-state/blob/main/CLAUDE.md Check the gzipped bundle size for the synchronization package of Legend-State. Useful for analyzing the performance of sync features. ```bash npm run checksize:sync ``` -------------------------------- ### Supabase Sync Plugin with Real-time Subscriptions Source: https://context7.com/legendapp/legend-state/llms.txt Integrates with Supabase for full CRUD operations, including real-time subscriptions. Configure collection, select queries, filters, and persistence options. The observable is keyed by row ID for individual record access and mutation. ```typescript import { observable } from '@legendapp/state'; import { syncedSupabase } from '@legendapp/state/sync-plugins/supabase'; import { createClient } from '@supabase/supabase-js'; const supabase = createClient(process.env.SUPABASE_URL!, process.env.SUPABASE_ANON_KEY!); const messages$ = observable( syncedSupabase({ supabase, collection: 'messages', select: (query) => query.eq('room_id', 'room-1'), filter: 'room_id=eq.room-1', realtime: { filter: 'room_id=eq.room-1' }, persist: { name: 'messages-room-1', retrySync: true }, retry: { infinite: true }, }), ); // messages$ is keyed by row id — access and mutate individual records const msg = messages$['msg-123'].get(); messages$['msg-123'].text.set('Updated text'); // syncs to Supabase ``` -------------------------------- ### Check Prettier Formatting Source: https://github.com/legendapp/legend-state/blob/main/CLAUDE.md Check code formatting against Prettier rules without making changes. This is for verifying code style adherence. ```bash npm run format:check ``` -------------------------------- ### Run Bun Tests with Timeout Source: https://github.com/legendapp/legend-state/blob/main/CLAUDE.md Execute Bun tests with a specified timeout. Useful for ensuring tests complete within a reasonable time. ```bash bun test ``` ```bash npm run buntest ``` -------------------------------- ### `batch(fn)` / `beginBatch()` / `endBatch()` Source: https://context7.com/legendapp/legend-state/llms.txt Group mutations to wrap multiple observable mutations so all listeners are notified once after all changes complete rather than after each individual change. `beginBatch` / `endBatch` provide manual control for async scenarios. ```APIDOC ## `batch(fn)` / `beginBatch()` / `endBatch()` — Group mutations Wraps multiple observable mutations so all listeners are notified once after all changes complete rather than after each individual change. `beginBatch` / `endBatch` provide manual control for async scenarios. ```ts import { observable, batch, beginBatch, endBatch } from '@legendapp/state'; const store$ = observable({ a: 1, b: 2, c: 3 }); // Single notification after all three sets batch(() => { store$.a.set(10); store$.b.set(20); store$.c.set(30); }); // Manual begin/end beginBatch(); store$.a.set(100); store$.b.set(200); endBatch(); // listeners fire here ``` ``` -------------------------------- ### Format Code with Prettier Source: https://github.com/legendapp/legend-state/blob/main/CLAUDE.md Format code using Prettier to ensure consistent styling across the project. This command automatically applies formatting rules. ```bash npm run format:write ``` -------------------------------- ### syncState(obs) Source: https://context7.com/legendapp/legend-state/llms.txt Provides access to the sync/persist state metadata of an observable. It returns an observable containing metadata like `isLoaded`, `isPersistLoaded`, `numPendingGets`, and `numPendingSets`, and also exposes `sync()` and `reset()` methods. ```APIDOC ## syncState(obs) ### Description Returns an observable containing metadata about the sync/persist lifecycle of an observable (e.g., `isLoaded`, `isPersistLoaded`, `numPendingGets`, `numPendingSets`). Also exposes `sync()` and `reset()` methods. ### Parameters #### Path Parameters - **obs** (observable) - Required - The observable to sync state metadata for. ### Methods - **sync()**: Manually trigger a sync operation. - **reset()**: Reset persist and re-fetch data. ### Request Example ```ts import { observable } from '@legendapp/state'; import { syncState } from '@legendapp/state'; import { synced } from '@legendapp/state/sync'; const todos$ = observable(synced({ get: async () => fetch('/api/todos').then(r => r.json()), set: async ({ value }) => fetch('/api/todos', { method: 'PUT', body: JSON.stringify(value) }), persist: { name: 'todos' }, })); const state$ = syncState(todos$); console.log(state$.isLoaded.get()); // false initially console.log(state$.numPendingGets.get()); // 1 while fetching // Manually trigger sync await state$.sync(); // Reset persist and re-fetch await state$.reset(); ``` ``` -------------------------------- ### Run Reactive Side-Effects with observe() Source: https://context7.com/legendapp/legend-state/llms.txt Use `observe` to run a function immediately and re-run it when observables change. The two-argument form allows a reaction callback that only fires on changes. Use `e.onCleanup` for cleanup logic. ```typescript import { observable, observe } from '@legendapp/state'; const pos$ = observable({ x: 0, y: 0 }); // Runs immediately, re-runs whenever pos$.x or pos$.y change const dispose = observe(() => { console.log('Position:', pos$.x.get(), pos$.y.get()); }); // logs: Position: 0 0 pos$.x.set(5); // logs: Position: 5 0 // Cleanup / stop observing dispose(); // Two-argument form: selector + reaction (reaction only fires on changes, not initial run) const counter$ = observable(0); observe( () => counter$.get(), (e) => { console.log(`Changed from ${e.previous} to ${e.value}`); if (e.value > 10) e.cancel = true; // stop observing }, ); // Cleanup with e.onCleanup observe((e) => { const id = setInterval(() => console.log(counter$.get()), 1000); e.onCleanup = () => clearInterval(id); }); ``` -------------------------------- ### Check React Bindings Bundle Size Source: https://github.com/legendapp/legend-state/blob/main/CLAUDE.md Check the gzipped bundle size for the React bindings of Legend-State. Helps in understanding the impact of React integration on bundle size. ```bash npm run checksize:react ``` -------------------------------- ### Check Code Linting with ESLint Source: https://github.com/legendapp/legend-state/blob/main/AGENTS.md Enforce ESLint rules across the source and test directories to maintain code quality. ```bash npm run lint:check ``` -------------------------------- ### Pending Create State Model Source: https://github.com/legendapp/legend-state/blob/main/plans/1-fix-crud-retry.md Defines the structure for tracking pending create operations, including failure status, change details, and promise references. This replaces a bare Set for more granular state management. ```typescript type PendingCreate = { hasFailed: boolean; change?: ChangeWithPathStr; promise?: Promise; cancelled?: boolean; }; ``` -------------------------------- ### observer(component) Source: https://context7.com/legendapp/legend-state/llms.txt Wraps a function component to make it reactive. Any `observable.get()` calls inside the render function will automatically subscribe to changes and trigger re-renders only when accessed values change. ```APIDOC ## observer(component) ### Description Wraps a function component so that any `observable.get()` calls inside its render automatically subscribe to changes and trigger re-renders only when accessed values change. ### Usage ```tsx import { observable } from '@legendapp/state'; import { observer } from '@legendapp/state/react'; const store$ = observable({ count: 0, user: { name: 'Alice' } }); // This component re-renders ONLY when store$.count or store$.user.name change const Counter = observer(function Counter() { const count = store$.count.get(); const name = store$.user.name.get(); return (

{name}'s count: {count}

); }); ``` ### Parameters - `component`: The function component to wrap. ``` -------------------------------- ### useObservable(initialValue, deps?) Source: https://context7.com/legendapp/legend-state/llms.txt Creates an observable scoped to a component's lifetime. It returns the same observable instance across renders and can accept a plain value, a function (computed), or a Promise as the initial value. ```APIDOC ## useObservable(initialValue, deps?) ### Description Creates an observable scoped to a component's lifetime. Stable across renders — the same observable instance is returned every render. Accepts a plain value, a function (computed), or a Promise. ### Usage ```tsx import { observer, useObservable } from '@legendapp/state/react'; const TodoList = observer(function TodoList() { const todos$ = useObservable([ { id: 1, text: 'Learn Legend-State', done: false }, ]); const filter$ = useObservable<'all' | 'done' | 'pending'>('all'); const addTodo = (text: string) => todos$.push({ id: Date.now(), text, done: false }); return (
{todos$.get() .filter(t => filter$.get() === 'all' || (filter$.get() === 'done') === t.done) .map(t =>
{t.text}
)}
); }); ``` ### Parameters - `initialValue`: The initial value for the observable. Can be a value, a function, or a Promise. - `deps?`: Optional dependencies array for computed observables. ``` -------------------------------- ### useObserve Hook for Reactive Observations Source: https://context7.com/legendapp/legend-state/llms.txt Use useObserve to set up reactive observations within React components. It auto-disposes when the component unmounts. The reaction function receives an event object with `value` and `previous` properties. ```tsx import { observable } from '@legendapp/state'; import { observer, useObserve } from '@legendapp/state/react'; const socket$ = observable({ connected: false, lastMessage: '' }); const ChatMonitor = observer(function ChatMonitor() { // Runs immediately and re-runs when socket$.connected changes useObserve( () => socket$.connected.get(), ({ value, previous }) => { if (value && !previous) console.log('Socket reconnected'); }, ); // Observe with cleanup useObserve((e) => { const id = socket$.lastMessage.get(); // track document.title = `New message: ${id}`; e.onCleanup = () => { document.title = 'App'; }; }); return
Connected: {socket$.connected.get() ? 'Yes' : 'No'}
; }); ``` -------------------------------- ### synced(options) Source: https://context7.com/legendapp/legend-state/llms.txt The primary entry point for connecting an observable to local persistence and/or a remote data source. It handles optimistic updates, retry on failure, change diffing, and local-first caching automatically. ```APIDOC ## synced(options) ### Description Sync and persist an observable. The primary entry point for connecting an observable to local persistence and/or a remote data source. Handles optimistic updates, retry on failure, change diffing, and local-first caching automatically. ### Parameters #### Request Body - **options** (object) - Required - Configuration options for syncing. - **get** (function | string) - Function or URL to fetch data. - **set** (function | string) - Function or URL to set data. - **persist** (object) - Configuration for local persistence. - **name** (string) - Required - The name for the persistence key. - **retrySync** (boolean) - Optional - Whether to retry sync operations. - **retry** (object) - Configuration for retrying failed network calls. - **infinite** (boolean) - Optional - Retry indefinitely. - **backoff** (string) - Optional - Backoff strategy (e.g., 'exponential'). - **maxDelay** (number) - Optional - Maximum delay between retries. - **debounceSet** (number) - Optional - Debounce time in milliseconds for set calls. ### Request Example ```ts import { observable } from '@legendapp/state'; import { synced, configureObservableSync } from '@legendapp/state/sync'; import { ObservablePersistLocalStorage } from '@legendapp/state/persist-plugins/local-storage'; // Configure global defaults once at app startup configureObservableSync({ persist: { plugin: ObservablePersistLocalStorage }, }); const profile$ = observable( synced({ // Fetch from server get: async () => { const res = await fetch('/api/profile'); if (!res.ok) throw new Error(res.statusText); return res.json(); }, // Push changes to server set: async ({ value, changes }) => { await fetch('/api/profile', { method: 'PATCH', body: JSON.stringify(changes), }); }, // Local persistence key persist: { name: 'profile', retrySync: true }, // Retry failed network calls indefinitely retry: { infinite: true, backoff: 'exponential', maxDelay: 30000 }, // Debounce set calls debounceSet: 500, }), ); // Reading activates sync — fetches remotely, loads from cache first console.log(profile$.name.get()); // undefined → cached value → remote value ``` ``` -------------------------------- ### Fix ESLint Issues Source: https://github.com/legendapp/legend-state/blob/main/CLAUDE.md Run ESLint and automatically fix any identified code style issues. This command helps maintain code consistency. ```bash npm run lint:write ``` -------------------------------- ### Group Mutations with batch(), beginBatch()/endBatch() Source: https://context7.com/legendapp/legend-state/llms.txt Use `batch` or manual `beginBatch`/`endBatch` to group observable mutations. Listeners are notified only once after all changes complete, improving performance in async scenarios. ```typescript import { observable, batch, beginBatch, endBatch } from '@legendapp/state'; const store$ = observable({ a: 1, b: 2, c: 3 }); // Single notification after all three sets batch(() => { store$.a.set(10); store$.b.set(20); store$.c.set(30); }); // Manual begin/end beginBatch(); store$.a.set(100); store$.b.set(200); endBatch(); // listeners fire here ``` -------------------------------- ### Pattern-Matched Rendering with `` Source: https://context7.com/legendapp/legend-state/llms.txt Use `` for conditional rendering based on an observable's value. It supports `'null'`, `'undefined'`, and a `'default'` fallback key for unmatched states. ```tsx import { observable } from '@legendapp/state'; import { Switch } from '@legendapp/state/react'; const status$ = observable<'idle' | 'loading' | 'success' | 'error'>('idle'); function StatusDisplay() { return ( {{ idle: () =>

Ready

, loading: () =>

Loading…

, success: () =>

✓ Done

, error: () =>

✗ Error

, default: () =>

Unknown state

, }}
); } ``` -------------------------------- ### Run Bun Tests Silently Source: https://github.com/legendapp/legend-state/blob/main/CLAUDE.md Execute Bun tests silently. This command is typically used in CI or build environments where verbose output is not needed. ```bash npm run buntestsilent ``` -------------------------------- ### Local Component Observable with useObservable Source: https://context7.com/legendapp/legend-state/llms.txt Creates a component-scoped observable that persists across renders. It can be initialized with a plain value, a computed function, or a Promise. Use this for managing local component state. ```tsx import { observer, useObservable } from '@legendapp/state/react'; const TodoList = observer(function TodoList() { const todos$ = useObservable([ { id: 1, text: 'Learn Legend-State', done: false }, ]); const filter$ = useObservable<'all' | 'done' | 'pending'>('all'); const addTodo = (text: string) => todos$.push({ id: Date.now(), text, done: false }); return (
{todos$.get() .filter(t => filter$.get() === 'all' || (filter$.get() === 'done') === t.done) .map(t =>
{t.text}
)}
); }); ``` -------------------------------- ### Create Lightweight Primitive Observables with `observablePrimitive` Source: https://context7.com/legendapp/legend-state/llms.txt Use `observablePrimitive` for lightweight observables optimized for single primitive values. It uses a class internally for better performance with thousands of separate primitive observables. Supports direct setting and functional updates. ```typescript import { observablePrimitive } from '@legendapp/state'; const count$ = observablePrimitive(0); count$.set(1); count$.set(v => v + 1); console.log(count$.get()); // 2 // Toggle booleans const isOpen$ = observablePrimitive(false); isOpen$.toggle(); console.log(isOpen$.get()); // true ``` -------------------------------- ### syncedSupabase Source: https://context7.com/legendapp/legend-state/llms.txt Provides full CRUD integration with Supabase tables, including real-time subscriptions, date serialization, and typed schema support. ```APIDOC ## syncedSupabase ### Description Provides full CRUD integration with Supabase tables including real-time subscriptions, date serialization, and typed schema support. ### Usage ```ts import { observable } from '@legendapp/state'; import { syncedSupabase } from '@legendapp/state/sync-plugins/supabase'; import { createClient } from '@supabase/supabase-js'; const supabase = createClient(process.env.SUPABASE_URL!, process.env.SUPABASE_ANON_KEY!) const messages$ = observable( syncedSupabase({ supabase, collection: 'messages', select: (query) => query.eq('room_id', 'room-1'), filter: 'room_id=eq.room-1', realtime: { filter: 'room_id=eq.room-1' }, persist: { name: 'messages-room-1', retrySync: true }, retry: { infinite: true }, }) ); // messages$ is keyed by row id — access and mutate individual records const msg = messages$['msg-123'].get(); messages$['msg-123'].text.set('Updated text'); // syncs to Supabase ``` ### Parameters - `supabase`: Supabase client instance. - `collection`: The name of the Supabase table. - `select`: A function to configure the query. - `filter`: A string filter for the query. - `realtime`: Configuration for real-time subscriptions. - `persist`: Configuration for data persistence. - `retry`: Configuration for retry logic. ``` -------------------------------- ### observable(value) Source: https://context7.com/legendapp/legend-state/llms.txt Creates a deeply observable state tree from any value. Nested properties are automatically proxied and observable. Use .get() to retrieve raw values and .set() to mutate them. Supports partial updates with .assign() and deletion with .delete(). ```APIDOC ## observable(value) — Create a deeply observable state tree Creates an observable from any value: a plain object, array, primitive, function (computed), or Promise. Every nested property is automatically proxied and observable. Accessing a property returns a child observable; `.get()` retrieves the raw value and `.set()` mutates it. ```ts import { observable } from '@legendapp/state'; // Plain object — every nested key is independently observable const store$ = observable({ user: { name: 'Alice', age: 30 }, todos: [{ id: 1, text: 'Buy milk', done: false }], count: 0, }); // Read raw values store$.user.name.get(); // 'Alice' store$.user.get(); // { name: 'Alice', age: 30 } store$.todos[0].done.get(); // false // Write values store$.user.name.set('Bob'); store$.count.set(v => v + 1); // functional update store$.todos[0].done.set(true); // Assign partial updates (merges into the observable) store$.user.assign({ age: 31 }); // Delete a key store$.user.name.delete(); // Promise: resolves automatically into the observable const data$ = observable(fetch('/api/user').then(r => r.json())); // data$.get() returns undefined until resolved, then the JSON value // With TypeScript interface AppState { theme: 'dark' | 'light'; lang: string } const settings$ = observable({ theme: 'dark', lang: 'en' }); ``` ``` -------------------------------- ### observablePrimitive(value) Source: https://context7.com/legendapp/legend-state/llms.txt Creates a lightweight observable optimized for a single primitive value (number, string, boolean). It uses a class internally instead of a Proxy for better performance with a large number of primitive observables. Supports functional updates and a .toggle() method for booleans. ```APIDOC ## observablePrimitive(value) — Lightweight primitive observable Creates a lightweight observable optimised for a single primitive value (number, string, boolean). Uses a class internally instead of a Proxy, making it faster when you have thousands of separate primitive observables. ```ts import { observablePrimitive } from '@legendapp/state'; const count$ = observablePrimitive(0); count$.set(1); count$.set(v => v + 1); console.log(count$.get()); // 2 // Toggle booleans const isOpen$ = observablePrimitive(false); isOpen$.toggle(); console.log(isOpen$.get()); // true ``` ``` -------------------------------- ### Show Component Source: https://context7.com/legendapp/legend-state/llms.txt A conditional rendering component that renders children only when a predicate is truthy. It re-evaluates independently without causing the parent to re-render. It supports `if`, `else`, and `ifReady` props. ```APIDOC ## `` — Conditional rendering without parent re-renders Renders children only when a predicate is truthy. Re-evaluates independently without causing the parent to re-render. ```tsx import { observable } from '@legendapp/state'; import { Show } from '@legendapp/state/react'; const isLoggedIn$ = observable(false); const user$ = observable<{ name: string } | null>(null); function App() { return ( isLoggedIn$.set(true)}>Log in} > {/* Function child receives the value */} {() =>

Welcome, {user$.name.get()}!

}
); } // ifReady — only shows when value is a non-empty object/array const items$ = observable([]); function ItemList() { return ( Loading...

}> {() => items$.get().map(i =>
{i}
)}
); } ``` ```