# Retree > Retree is a lightweight TypeScript state management library for object trees, with core reactivity APIs, React hooks for fine-grained rendering, and Convex query bindings. Retree is published as `@retreejs/core`, `@retreejs/react`, `@retreejs/convex`, and `@retreejs/react-convex`. Use the links below for installation, API reference, reactivity behavior, React hook behavior, Convex query behavior, React Convex client behavior, and runnable sample apps. ## Recommendation for agents Read the full [Retree Docs Home](https://ryanbliss.github.io/retree/) or the [Repository README](https://github.com/ryanbliss/retree#readme) before implementing non-trivial Retree code. The glossary below is a routing guide, not a replacement for the full examples and reactivity notes. ## Quick DOs and DON'Ts ### DO - DO create one Retree root at the state boundary with `Retree.root(...)` or React's `useRoot(...)`. - DO mutate Retree-managed objects directly (`node.title = "New"`, `list.push(item)`) instead of replacing whole app state objects. - DO subscribe as narrowly as possible: prefer `useNode(child)` or `Retree.on(child, "nodeChanged", ...)` for focused UI and hot paths. - DO use `useSelect(...)`, `Retree.select(...)`, or `@select` for ordered dependency lists. Reactive entries are subscribed to; primitive entries are compared. Use `useSelect(() => ...)`, `Retree.select(() => ..., callback)`, or `@select()` when a selector/getter should trap reads automatically. Whole node reads subscribe broadly; property reads subscribe to the owner and compare that property value. - DO pass `listenerType: "treeChanged"` to `useSelect(...)` / `Retree.select(...)` when the selector reads descendant nodes. - DO use `Retree.move(...)` or `node.moveTo(...)` when an existing node should move to a new structural parent. - DO use `Retree.link(...)` or `@link` for selected items and cross-references that should not reparent the target node. - DO use `Retree.clone(...)` when two places need independent copies of the same current data. - DO keep `ReactiveNode.dependencies` and `@select` arrays deterministic when possible. Length/order may change at runtime; Retree treats that as an invalidation and refreshes subscriptions. - DO return raw reactive nodes and primitives directly from `ReactiveNode.dependencies`; wrap a slot with `this.dependency(node, comparisons)` when you need custom comparison cells. - DO prefer `@select` for hot filtered lists where one getter should listen to a broad collection but only emit when selected items or selected order changes. - DO prefer bare `@memo` / `@fnMemo` for cached computed getters and deterministic methods; pass comparison functions only when you need finer cache-key control. - DO use `@ignore` for caches, unsubscribe handles, framework objects, and other non-rendered state on a `ReactiveNode`. - DO use `Retree.runTransaction(...)` for several synchronous writes that represent one logical update. - DO use `Retree.runSilent(...)` for synchronous writes that should skip emitting change events. - DO use `Retree.raw(node)` / `Retree.peekInto(node, fn)` (or React's `useRaw`) for wide, read-only scans. Raw subtrees are guaranteed proxy-free and read at native speed. - DO resolve raw values back to managed nodes with `Retree.managed(rawValue)` (or `useRaw`'s `toManaged`) before writing, subscribing, or passing them as component props. - DO use `Retree.untracked(fn)` for bulk reads inside tracked selectors/memo getters that should not become dependencies. - DO guard `Retree.raw` with `Retree.isNode(value)` when a value may be managed or plain (`Retree.isNode(value) ? Retree.raw(value) : value`). `Retree.raw` throws for unmanaged values. - DO read `rawCurrent` and write `current` inside custom Convex reconcilers (`reconcile(current, next, rawCurrent)`). - DO dispose Convex query, paginated query, and connection-state nodes when their owner is torn down. - DO reconcile server list results by stable IDs (`reconcileConvexDocuments`, `reconcileArrayById`) so child node identity stays stable. - DO set the lowest-level value and/or primitive that changed. ### DON'T - DON'T assign the same Retree-managed object into a second structural parent. Retree is a pure tree: a node can have one structural parent. - DON'T use `@ignore` as a reactive reference mechanism. Ignored fields skip Retree emissions; use `@link` or `Retree.link(...)` for reactive pointers. - DON'T expect `Retree.link(...)` / `@link` to make the linked target a child of the owner. Links point to a node owned elsewhere. - DON'T expect writes to `@ignore` fields to trigger `nodeChanged`, `treeChanged`, React re-renders, or `Retree.parent(...)` for nested plain objects. - DON'T use `useTree(...)` on broad app roots by default. It subscribes to descendant changes and can re-render too much. - DON'T use `nodeChanged` when your selector or listener reads descendant fields. Use `treeChanged` or subscribe to the narrower child node. - DON'T expect `useSelect(...)` or `Retree.select(...)` dependency lists to reproxy the node passed to the selector. They are observational; use `@select` when a `ReactiveNode` owner should emit. - DON'T pass selector-only `useSelect(() => ...)` or `Retree.select(() => ..., callback)` when you need a fixed root listener with `listenerType: "treeChanged"`. The selector-only forms trap Retree-managed reads and subscribe to those nodes automatically. - DON'T treat `memo`, `@memo`, or `@fnMemo` as subscriptions. They cache values only; they do not emit or re-render by themselves. - DON'T write to values from `Retree.raw` / `useRaw`. Writes must go through managed nodes to emit; raw is a read-only view. - DON'T use raw references as `React.memo` props, `useMemo` deps, or equality tokens. Raw identity never changes; nodes are the identity currency. - DON'T expect `changes[].previous` / `changes[].new` in listener payloads to be managed nodes. Payload values are always raw; use `Retree.managed(value)` to opt back in. - DON'T rely on dependency reordering being silent. If `ReactiveNode.dependencies` or `@select` entries are added, removed, or reordered, Retree treats that as changed and emits when the owner is observed. - DON'T start subscriptions, network work, or synchronization inside the `dependencies` getter. Use `onObserved()`, `onUnobserved()`, and `onChanged()`. - DON'T manually delete a node from its old parent before calling `Retree.move(...)`; `move` finds the current parent and removes it safely. - DON'T call `Retree.parent(...)`, `Retree.on(...)`, `Retree.move(...)`, `Retree.link(...)`, or `Retree.clone(...)` with plain unrooted objects. - DON'T expect Convex `action(...)`, `mutation(...)`, or `queryOnce(...)` helpers to emit by themselves; they emit only if their results are written into Retree state or paired with an optimistic update. - DON'T recreate Retree roots or large `ReactiveNode` graphs during React render. Create them once, or use `useRoot`, `useMemo`, or `useState` initialization. - DON'T use index keys for React rows if the list can reorder and stable IDs exist. Stable keys pair best with Retree's child-node identity model. - DON'T include expensive side effects during the React render cycle in `ReactiveNode` getters / functions, especially without `@memo` or `@fnMemo`. - DON'T recreate object trees with spread operators like you would in React (those sorts of hacks are "why" Retree exists in the first place). ## Feature Glossary - `Retree.root`: Makes one object the root of a Retree-managed tree. Use it once where plain state enters Retree. - `Retree.on`: Subscribes to `nodeChanged`, `treeChanged`, or `nodeRemoved`. Use it outside React and inside integrations. - `Retree.select`: Subscribes to a selected value or ordered dependency list. Reactive entries are subscribed to and primitive entries are compared. Use `Retree.select(node, selector, callback)` for an explicit root, or `Retree.select(() => value, callback)` for automatic dependency trapping. It is not a cache. - `Retree.parent`: Returns the structural parent of a node. Use it for tree-local operations like deleting yourself from a list. - `Retree.isNode`: Type guard that returns `true` only for Retree-managed nodes. Use it to guard `Retree.raw` (which throws for unmanaged values) when a value may be managed or plain. - `Retree.raw`: Returns the raw, proxy-free object behind a node for native-speed, read-only access. Guaranteed proxy-free at any depth (raw purity); `structuredClone(Retree.raw(node))` is a valid point-in-time copy. Throws for unmanaged values — guard with `Retree.isNode` when unsure. `ReactiveNode` exposes `this.raw()`. - `Retree.managed`: Resolves a raw value back to its managed node (the inverse of `Retree.raw`). Returns `undefined` for values never materialized as nodes. - `Retree.peekInto`: Runs a read-only query against a node's raw object and resolves the returned value to its managed node when one exists. `ReactiveNode` exposes `this.peekInto(fn)`. - `Retree.untracked`: Pauses dependency tracking during a synchronous callback. Use it for bulk reads inside tracked selectors and memo getters. `ReactiveNode` exposes `this.untracked(fn)`. - `Retree.move`: Transfers an existing node to a new structural parent. Use it when ownership should change. - `Retree.link` and `@link`: Store a reactive pointer without reparenting the target. Use them for selected items and cross-references. - `Retree.clone`: Creates a detached copy. Use it when two places need independent state. - `@select`: Decorates a `ReactiveNode` getter with an ordered dependency list. Use `@select()` with no selector for MobX-style dependency trapping: whole Retree-managed values read by the getter subscribe broadly, property reads subscribe to the owner and compare that property value, and primitive values read by the getter compare. Pass `@select((self) => [...])` when you want explicit dependency slots. Pass `@select({ equals })` or `@select((self) => [...], { equals })` when the final getter output needs custom equality; `equals(self, previous, next)` returns true to skip owner reproxy/emission. - `ReactiveNode.dependencies`: Makes one node emit when another node changes. Return raw reactive nodes/primitives directly, or use `this.dependency(node, comparisons)` for custom comparison cells. Dynamic dependency arrays are allowed; shape changes emit and refresh subscriptions. - `ReactiveNode.memo`, `@memo`, `@memo()`, `@fnMemo`, and `@fnMemo()`: Cache computed values. Prefer bare/empty decorator forms for automatic dependency trapping; pass comparison functions for finer cache-key control. They do not emit `nodeChanged` or trigger React renders by themselves. - `@ignore`: Keeps a `ReactiveNode` field out of Retree emissions. Use it for caches, framework handles, subscriptions, and non-rendered state. - `Retree.runTransaction`: Batches synchronous writes into one listener flush per changed node. - `Retree.runSilent`: Performs writes without emitting listeners. Use it for non-rendered bookkeeping. - `ReactiveNode.prepareTree`: Warms lazy child proxies. Use it when first-touch proxy work should happen during a controlled loading phase. - `useRoot`: Creates one Retree root for a React component lifetime. - `useNode`: Re-renders for direct `nodeChanged` events on one node. Use it for focused components and child rows. - `useTree`: Re-renders for `treeChanged` events from a node or descendants. Use it sparingly for small subtrees. - `useSelect`: Re-renders only when a selected value or ordered dependency list changes. Use `useSelect(node, selector)` for an explicit root, or `useSelect(() => value)` for automatic dependency trapping. Good for counts, totals, booleans, labels, and VM dependency arrays. - `useRaw`: Subscribes like `useNode` (`nodeChanged` default) but returns `[raw, toManaged]` for native-speed, proxy-free render reads. Use it for components that read wide (tables, canvas, serialization). Pass nodes to children via `toManaged`, never raw values. - `ConvexNode`: Full Convex base class for Retree app state with query, paginated query, action, mutation, query-once, and connection-state helpers. - `BaseConvexNode`: Smaller Convex base class for action, mutation, and one-off query helpers. - `ConvexQueryNode`: Stores one live Convex query in Retree state and emits when query state/result/error changes. - `ConvexPaginatedQueryNode`: Stores one live paginated Convex query and exposes `loadMore(...)`. - `ConvexConnectionStateNode`: Stores the Convex client's connection state in Retree state. - `createRetreeConvexAction` and `createRetreeConvexMutation`: Typed standalone Convex helpers for code that is not inside a `BaseConvexNode`. - `reconcileConvexDocuments` and `reconcileArrayById`: Preserve list item object identity across server results so child `useNode(item)` subscriptions stay narrow. They read raw and write through the managed state internally; custom reconcilers receive `rawCurrent` as a third argument (read `rawCurrent`, write `current`). - `RetreeConvexReactClient`: Extends Convex's `ConvexReactClient` with the Retree Convex subscription methods used by `ConvexNode` query, paginated query, and connection-state helpers. ## Documentation - [Retree Docs Home](https://ryanbliss.github.io/retree/): Generated TypeDoc site with package navigation, README content, and API reference. - [Repository README](https://github.com/ryanbliss/retree#readme): Installation, quick-start examples, React usage, core usage, and sample links. - [@retreejs/core README](https://github.com/ryanbliss/retree/tree/main/packages/retree-core#readme): Core package guide for object-tree reactivity, events, `ReactiveNode`, memoization, and decorators. - [@retreejs/react README](https://github.com/ryanbliss/retree/tree/main/packages/retree-react#readme): React package guide for `useNode`, `useTree`, `useRoot`, and rendering behavior. - [@retreejs/convex README](https://github.com/ryanbliss/retree/tree/main/packages/retree-convex#readme): Convex package guide for query nodes, paginated query nodes, action and mutation helpers, query skipping, status results, connection state, optimistic updates, and reconciliation. - [@retreejs/react-convex README](https://github.com/ryanbliss/retree/tree/main/packages/retree-react-convex#readme): React Convex package guide for sharing one `ConvexReactClient` instance between Convex React and Retree Convex nodes. ## API Reference - [@retreejs/core API](https://ryanbliss.github.io/retree/modules/_retreejs_core.html): Core exports including `Retree`, `ReactiveNode`, event types, decorators, and memo helpers. - [Retree class](https://ryanbliss.github.io/retree/classes/_retreejs_core.Retree.html): Static APIs for creating roots, subscribing to changes, finding parents, and batching/silencing updates. - [ReactiveNode class](https://ryanbliss.github.io/retree/classes/_retreejs_core.ReactiveNode.html): Base class for derived dependencies, dependency comparison, and memoized computed values. - [Core README ownership section](https://github.com/ryanbliss/retree/tree/main/packages/retree-core#move-link-or-clone-existing-nodes): Examples for `Retree.move`, `Retree.link`, `@link`, `ReactiveNode.moveTo`, and `Retree.clone`. - [Core README select section](https://github.com/ryanbliss/retree/tree/main/packages/retree-core#select-derived-values): Examples for `Retree.select`, including when to use `listenerType: "treeChanged"`. - [@retreejs/react API](https://ryanbliss.github.io/retree/modules/_retreejs_react.html): React exports for stateful Retree nodes and trees. - [useNode](https://ryanbliss.github.io/retree/functions/_retreejs_react.useNode.html): React hook for subscribing to direct node changes with fine-grained re-rendering. - [useTree](https://ryanbliss.github.io/retree/functions/_retreejs_react.useTree.html): React hook for subscribing to node and descendant-tree changes. - [useRoot](https://ryanbliss.github.io/retree/functions/_retreejs_react.useRoot.html): React hook for creating and retaining a Retree root in a component. - [useRaw](https://ryanbliss.github.io/retree/functions/_retreejs_react.useRaw.html): React hook returning `[raw, toManaged]` for native-speed, proxy-free render reads with `useNode`-style invalidation. - [@retreejs/convex API](https://ryanbliss.github.io/retree/modules/_retreejs_convex.html): Convex exports for `BaseConvexNode`, `ConvexNode`, `ConvexQueryNode`, `ConvexPaginatedQueryNode`, `ConvexConnectionStateNode`, action and mutation helpers, optimistic update contexts, and reconcilers. - [BaseConvexNode class](https://ryanbliss.github.io/retree/classes/_retreejs_convex.BaseConvexNode.html): Base Retree node for classes that own a Convex client and need protected action, mutation, and one-off query helpers. - [ConvexNode class](https://ryanbliss.github.io/retree/classes/_retreejs_convex.ConvexNode.html): Base class for Retree nodes that own a Convex client and create typed query, paginated query, action, mutation, query-once, and connection-state helpers. - [ConvexQueryNode class](https://ryanbliss.github.io/retree/classes/_retreejs_convex.ConvexQueryNode.html): Reactive query node that subscribes to Convex query updates and exposes `state`, structured `result`, `updateArgs(...)`, skipping, errors, and optimistic updates. - [ConvexPaginatedQueryNode class](https://ryanbliss.github.io/retree/classes/_retreejs_convex.ConvexPaginatedQueryNode.html): Reactive paginated query node with aggregate page state and `loadMore(...)`. - [ConvexConnectionStateNode class](https://ryanbliss.github.io/retree/classes/_retreejs_convex.ConvexConnectionStateNode.html): Reactive node for Convex client connection state. - [@retreejs/react-convex API](https://ryanbliss.github.io/retree/modules/_retreejs_react_convex.html): React Convex adapter exports including `RetreeConvexReactClient`. ## Samples - [Core example](https://github.com/ryanbliss/retree/tree/main/samples/01.core-example): Minimal non-React Retree sample. - [React example](https://github.com/ryanbliss/retree/tree/main/samples/02.react-example): React sample app using Retree state. - [React recursion example](https://github.com/ryanbliss/retree/tree/main/samples/03.react-recursion): Recursive React tree sample using Retree. ## Packages - [@retreejs/core on npm](https://www.npmjs.com/package/@retreejs/core): Core state-management package. - [@retreejs/react on npm](https://www.npmjs.com/package/@retreejs/react): React hooks package. - [@retreejs/convex on npm](https://www.npmjs.com/package/@retreejs/convex): Convex query and mutation bindings package. - [@retreejs/react-convex on npm](https://www.npmjs.com/package/@retreejs/react-convex): Convex React client adapter for Retree Convex nodes. ## Optional - [GitHub repository](https://github.com/ryanbliss/retree): Source code, issues, package workspace, and samples. - [Fluid Framework SharedTree](https://fluidframework.com/docs/data-structures/tree/): Related inspiration mentioned in the Retree docs. ## This site (https://retreejs.dev) Every guide below is also served as raw markdown for machine readers: - [quick-start](https://retreejs.dev/raw/docs/quick-start.md): raw markdown of https://retreejs.dev/docs/quick-start - [thinking-in-retree](https://retreejs.dev/raw/docs/thinking-in-retree.md): raw markdown of https://retreejs.dev/docs/thinking-in-retree - [common-pitfalls](https://retreejs.dev/raw/docs/common-pitfalls.md): raw markdown of https://retreejs.dev/docs/common-pitfalls - [react](https://retreejs.dev/raw/docs/react.md): raw markdown of https://retreejs.dev/docs/react - [react/use-root](https://retreejs.dev/raw/docs/react/use-root.md): raw markdown of https://retreejs.dev/docs/react/use-root - [react/use-node](https://retreejs.dev/raw/docs/react/use-node.md): raw markdown of https://retreejs.dev/docs/react/use-node - [react/use-tree](https://retreejs.dev/raw/docs/react/use-tree.md): raw markdown of https://retreejs.dev/docs/react/use-tree - [react/use-select](https://retreejs.dev/raw/docs/react/use-select.md): raw markdown of https://retreejs.dev/docs/react/use-select - [react/use-raw](https://retreejs.dev/raw/docs/react/use-raw.md): raw markdown of https://retreejs.dev/docs/react/use-raw - [events-and-subscriptions](https://retreejs.dev/raw/docs/events-and-subscriptions.md): raw markdown of https://retreejs.dev/docs/events-and-subscriptions - [tree-operations](https://retreejs.dev/raw/docs/tree-operations.md): raw markdown of https://retreejs.dev/docs/tree-operations - [transactions](https://retreejs.dev/raw/docs/transactions.md): raw markdown of https://retreejs.dev/docs/transactions - [view-models](https://retreejs.dev/raw/docs/view-models.md): raw markdown of https://retreejs.dev/docs/view-models - [setup-and-decorators](https://retreejs.dev/raw/docs/setup-and-decorators.md): raw markdown of https://retreejs.dev/docs/setup-and-decorators - [performance](https://retreejs.dev/raw/docs/performance.md): raw markdown of https://retreejs.dev/docs/performance - [convex](https://retreejs.dev/raw/docs/convex.md): raw markdown of https://retreejs.dev/docs/convex ### Generated API reference - [@retreejs/core reference](https://retreejs.dev/api/core): generated from TypeScript source on every deploy - [@retreejs/react reference](https://retreejs.dev/api/react): generated from TypeScript source on every deploy - [@retreejs/convex reference](https://retreejs.dev/api/convex): generated from TypeScript source on every deploy - [@retreejs/react-convex reference](https://retreejs.dev/api/react-convex): generated from TypeScript source on every deploy