-
Add Intent agent skills (SKILL.md files) to guide AI coding agents. Include skills for core DB concepts, all 5 framework bindings, meta-framework integration, and offline transactions. Also add
export * from '@tanstack/db'to angular-db for consistency with other framework packages. (#1330) -
fix(react-db): make getNextPageParam optional in useLiveInfiniteQuery (#1268)
-
fix(react-db): handle rejected/stale setWindow promises in useLiveInfiniteQuery (#1269)
-
Updated dependencies [
bf1d078]:- @tanstack/db@0.5.31
- Updated dependencies [
e9d0fd8]:- @tanstack/db@0.5.30
- Updated dependencies [
46450e7]:- @tanstack/db@0.5.28
-
Fix
useLiveInfiniteQuerypeek-ahead detection forhasNextPage. The initial query now correctly requestspageSize + 1items to detect whether additional pages exist, matching the behavior of subsequent page loads. (#1209)Fix async on-demand pagination by ensuring the graph callback fires at least once even when there is no pending graph work, so that
loadMoreIfNeededis triggered aftersetWindow()increases the limit. -
Updated dependencies [
85c373e,9184dcc,83d5ac8]:- @tanstack/db@0.5.26
- Updated dependencies [
7099459]:- @tanstack/db@0.5.24
- Updated dependencies [
05130f2]:- @tanstack/db@0.5.23
- Updated dependencies [
f9b741e]:- @tanstack/db@0.5.22
- Updated dependencies []:
- @tanstack/db@0.5.20
- Updated dependencies [
c1247e8]:- @tanstack/db@0.5.18
-
Improve runtime error message and documentation when
useLiveSuspenseQueryreceivesundefinedfrom query callback. (#860)Following TanStack Query's
useSuspenseQuerydesign,useLiveSuspenseQueryintentionally does not support disabled queries (when callback returnsundefinedornull). This maintains the type guarantee thatdatais alwaysT(notT | undefined), which is a core benefit of using Suspense.What changed:
- Improved runtime error message with clear guidance:
useLiveSuspenseQuery does not support disabled queries (callback returned undefined/null). The Suspense pattern requires data to always be defined (T, not T | undefined). Solutions: 1) Use conditional rendering - don't render the component until the condition is met. 2) Use useLiveQuery instead, which supports disabled queries with the 'isEnabled' flag.- Enhanced JSDoc documentation with detailed
@remarkssection explaining the design decision, showing both incorrect (❌) and correct (✅) patterns
Why this matters:
// ❌ This pattern doesn't work with Suspense queries: const { data } = useLiveSuspenseQuery( (q) => userId ? q.from({ users }).where(({ users }) => eq(users.id, userId)).findOne() : undefined, [userId] ) // ✅ Instead, use conditional rendering: function UserProfile({ userId }: { userId: string }) { const { data } = useLiveSuspenseQuery( (q) => q.from({ users }).where(({ users }) => eq(users.id, userId)).findOne(), [userId] ) return <div>{data.name}</div> // data is guaranteed non-undefined } function App({ userId }: { userId?: string }) { if (!userId) return <div>No user selected</div> return <UserProfile userId={userId} /> } // ✅ Or use useLiveQuery for conditional queries: const { data, isEnabled } = useLiveQuery( (q) => userId ? q.from({ users }).where(({ users }) => eq(users.id, userId)).findOne() : undefined, [userId] )
This aligns with TanStack Query's philosophy where Suspense queries prioritize type safety and proper component composition over flexibility.
-
Updated dependencies [
f795a67,d542667,6503c09,b1cc4a7]:- @tanstack/db@0.5.17
- Updated dependencies [
41308b8]:- @tanstack/db@0.5.16
- Updated dependencies [
32ec4d8]:- @tanstack/db@0.5.15
- Updated dependencies [
26ed0aa]:- @tanstack/db@0.5.14
-
Fixed
isReadyto returntruefor disabled queries inuseLiveQuery/injectLiveQueryacross all framework packages. When a query function returnsnullorundefined(disabling the query), there's no async operation to wait for, so the hook should be considered "ready" immediately. (#886)Additionally, all frameworks now have proper TypeScript overloads that explicitly support returning
undefined | nullfrom query functions, making the disabled query pattern type-safe.This fixes the common pattern where users conditionally enable queries and don't want to show loading states when the query is disabled.
-
Updated dependencies [
c4b9399,a1a484e]:- @tanstack/db@0.5.11
- Updated dependencies [
5f474f1]:- @tanstack/db@0.5.9
- Updated dependencies [
295cb45]:- @tanstack/db@0.5.7
- Updated dependencies [
c8a2c16]:- @tanstack/db@0.5.6
- Updated dependencies [
077fc1a]:- @tanstack/db@0.5.5
- Updated dependencies [
99a3716]:- @tanstack/db@0.5.2
- Updated dependencies [
a83a818]:- @tanstack/db@0.5.1
- Updated dependencies [
243a35a,f9d11fc,7aedf12,28f81b5,28f81b5,f6ac7ea,01093a7]:- @tanstack/db@0.5.0
- Updated dependencies [
75470a8]:- @tanstack/db@0.4.19
-
Add
useLiveSuspenseQueryhook for React Suspense support (#697)Introduces a new
useLiveSuspenseQueryhook that integrates with React Suspense and Error Boundaries, following TanStack Query'suseSuspenseQuerypattern.Key features:
- React 18+ compatible using the throw promise pattern
- Type-safe API with guaranteed data (never undefined)
- Automatic error handling via Error Boundaries
- Reactive updates after initial load via useSyncExternalStore
- Support for dependency-based re-suspension
- Works with query functions, config objects, and pre-created collections
Example usage:
import { Suspense } from 'react' import { useLiveSuspenseQuery } from '@tanstack/react-db' function TodoList() { // Data is guaranteed to be defined - no isLoading needed const { data } = useLiveSuspenseQuery((q) => q .from({ todos: todosCollection }) .where(({ todos }) => eq(todos.completed, false)), ) return ( <ul> {data.map((todo) => ( <li key={todo.id}>{todo.text}</li> ))} </ul> ) } function App() { return ( <Suspense fallback={<div>Loading...</div>}> <TodoList /> </Suspense> ) }
Implementation details:
- Throws promises when collection is loading (caught by Suspense)
- Throws errors when collection fails (caught by Error Boundary)
- Reuses promise across re-renders to prevent infinite loops
- Detects dependency changes and creates new collection/promise
- Same TypeScript overloads as useLiveQuery for consistency
Documentation:
- Comprehensive guide in live-queries.md covering usage patterns and when to use each hook
- Comparison with useLiveQuery showing different approaches to loading/error states
- Router loader pattern recommendation for React Router/TanStack Router users
- Error handling examples with Suspense and Error Boundaries
Resolves #692
- Updated dependencies [
49bcaa5]:- @tanstack/db@0.4.17
-
Add paced mutations with pluggable timing strategies (#704)
Introduces a new paced mutations system that enables optimistic mutations with pluggable timing strategies. This provides fine-grained control over when and how mutations are persisted to the backend. Powered by TanStack Pacer.
Key Design:
- Debounce/Throttle: Only one pending transaction (collecting mutations) and one persisting transaction (writing to backend) at a time. Multiple rapid mutations automatically merge together.
- Queue: Each mutation creates a separate transaction, guaranteed to run in the order they're made (FIFO by default, configurable to LIFO).
Core Features:
- Pluggable Strategy System: Choose from debounce, queue, or throttle strategies to control mutation timing
- Auto-merging Mutations: Multiple rapid mutations on the same item automatically merge for efficiency (debounce/throttle only)
- Transaction Management: Full transaction lifecycle tracking (pending → persisting → completed/failed)
- React Hook:
usePacedMutationsfor easy integration in React applications
Available Strategies:
debounceStrategy: Wait for inactivity before persisting. Only final state is saved. (ideal for auto-save, search-as-you-type)queueStrategy: Each mutation becomes a separate transaction, processed sequentially in order (defaults to FIFO, configurable to LIFO). All mutations are guaranteed to persist. (ideal for sequential workflows, rate-limited APIs)throttleStrategy: Ensure minimum spacing between executions. Mutations between executions are merged. (ideal for analytics, progress updates)
Example Usage:
import { usePacedMutations, debounceStrategy } from '@tanstack/react-db' const mutate = usePacedMutations({ mutationFn: async ({ transaction }) => { await api.save(transaction.mutations) }, strategy: debounceStrategy({ wait: 500 }), }) // Trigger a mutation const tx = mutate(() => { collection.update(id, (draft) => { draft.value = newValue }) }) // Optionally await persistence await tx.isPersisted.promise
-
Updated dependencies [
979a66f,f8a979b,cb25623]:- @tanstack/db@0.4.16
- Updated dependencies [
6738247]:- @tanstack/db@0.4.15
- Updated dependencies [
970616b]:- @tanstack/db@0.4.14
- Updated dependencies [
3c9526c]:- @tanstack/db@0.4.13
-
Add support for pre-created live query collections in useLiveInfiniteQuery, enabling router loader patterns where live queries can be created, preloaded, and passed to components. (#684)
-
Updated dependencies [
5566b26]:- @tanstack/db@0.4.11
-
Add
useLiveInfiniteQueryhook for infinite scrolling with live updates. (#669)The new
useLiveInfiniteQueryhook provides an infinite query pattern similar to TanStack Query'suseInfiniteQuery, but with live updates from your local collection. It usesliveQueryCollection.utils.setWindow()internally to efficiently paginate through ordered data without recreating the query on each page fetch.Key features:
- Automatic live updates as data changes in the collection
- Efficient pagination using dynamic window adjustment
- Peek-ahead mechanism to detect when more pages are available
- Compatible with TanStack Query's infinite query API patterns
Example usage:
import { useLiveInfiniteQuery } from '@tanstack/react-db' function PostList() { const { data, pages, fetchNextPage, hasNextPage, isLoading } = useLiveInfiniteQuery( (q) => q .from({ posts: postsCollection }) .orderBy(({ posts }) => posts.createdAt, 'desc'), { pageSize: 20, getNextPageParam: (lastPage, allPages) => lastPage.length === 20 ? allPages.length : undefined, }, ) if (isLoading) return <div>Loading...</div> return ( <div> {pages.map((page, i) => ( <div key={i}> {page.map((post) => ( <PostCard key={post.id} post={post} /> ))} </div> ))} {hasNextPage && ( <button onClick={() => fetchNextPage()}>Load More</button> )} </div> ) }
Requirements:
- Query must include
.orderBy()for the window mechanism to work - Returns flattened
dataarray andpagesarray for flexible rendering - Automatically detects new pages when data is synced to the collection
-
Updated dependencies [
63aa8ef,b0687ab]:- @tanstack/db@0.4.10
-
Refactored live queries to execute eagerly during sync. Live queries now materialize their results immediately as data arrives from source collections, even while those collections are still in a "loading" state, rather than waiting for all sources to be "ready" before executing. (#658)
-
Updated dependencies [
d9ae7b7,44555b7]:- @tanstack/db@0.4.8
- Updated dependencies [
6692aad]:- @tanstack/db@0.4.7
- Updated dependencies [
7556fb6]:- @tanstack/db@0.4.5
- Updated dependencies [
32f2212]:- @tanstack/db@0.4.3
- Updated dependencies [
8cd0876]:- @tanstack/db@0.4.1
-
Let collection.subscribeChanges return a subscription object. Move all data loading code related to optimizations into that subscription object. (#564)
-
Updated dependencies [
2f87216,ac6250a,2f87216]:- @tanstack/db@0.4.0
-
Expand
useLiveQuerycallback to support conditional queries and additional return types, enabling the ability to temporarily disable the query. (#535)New Features:
- Callback can now return
undefinedornullto temporarily disable the query - Callback can return a pre-created
Collectioninstance to use it directly - Callback can return a
LiveQueryCollectionConfigobject for advanced configuration - When disabled (returning
undefined/null), the hook returns a specific idle state
Usage Examples:
// Conditional queries - disable when not ready const enabled = useState(false) const { data, state, isIdle } = useLiveQuery((q) => { if (!enabled) return undefined // Disables the query return q.from({ users }).where(...) }, [enabled]) /** * When disabled, returns: * { * state: undefined, * data: undefined, * isIdle: true, * ... * } */ // Return pre-created Collection const { data } = useLiveQuery((q) => { if (usePrebuilt) return myCollection // Use existing collection return q.from({ items }).select(...) }, [usePrebuilt]) // Return LiveQueryCollectionConfig const { data } = useLiveQuery((q) => { return { query: q.from({ items }).select(...), id: `my-collection`, } })
- Callback can now return
-
Updated dependencies [
cacfca2]:- @tanstack/db@0.3.2
- Updated dependencies [
5f51f35]:- @tanstack/db@0.3.1
- Updated dependencies [
b162556]:- @tanstack/db@0.2.3
- Updated dependencies [
33515c6]:- @tanstack/db@0.2.2
- Updated dependencies [
620ebea]:- @tanstack/db@0.2.1
- Updated dependencies [
cc4c34a]:- @tanstack/db@0.1.12
-
Fixed a bug where a race condition could cause initial results not to be rendered when using
useLiveQuery. (#485) -
Updated dependencies [
b869f68]:- @tanstack/db@0.1.11
- Updated dependencies [
d64b4a8]:- @tanstack/db@0.1.9
- Updated dependencies [
ad33e9e]:- @tanstack/db@0.1.6
-
ensure that useLiveQuery returns a stable ref when there are no changes (#388)
-
Updated dependencies [
9a5a20c]:- @tanstack/db@0.1.5
-
Ensure that the ready status is correctly returned from a live query (#390)
-
Updated dependencies [
c90b4d8,6c1c19c,69a6d2d,6250a92,68538b4]:- @tanstack/db@0.1.4
- Updated dependencies [
0cb7699]:- @tanstack/db@0.1.3
- 0.1 release - first beta 🎉 (#332)
-
We have moved development of the differential dataflow implementation from @electric-sql/d2mini to a new @tanstack/db-ivm package inside the tanstack db monorepo to make development simpler. (#330)
-
Updated dependencies [
7d2f4be,f0eda36]:- @tanstack/db@0.1.0
- Updated dependencies [
6e8d7f6]:- @tanstack/db@0.0.33
- Updated dependencies [
e04bd12]:- @tanstack/db@0.0.32
- Updated dependencies [
3e9a36d]:- @tanstack/db@0.0.31
- Updated dependencies [
6bdde55]:- @tanstack/db@0.0.30
- Updated dependencies [
bec8620]:- @tanstack/db@0.0.27
-
Add initial release of TrailBase collection for TanStack DB. TrailBase is a blazingly fast, open-source alternative to Firebase built on Rust, SQLite, and V8. It provides type-safe REST and realtime APIs with sub-millisecond latencies, integrated authentication, and flexible access control - all in a single executable. This collection type enables seamless integration with TrailBase backends for high-performance real-time applications. (#228)
-
Updated dependencies [
09c6995]:- @tanstack/db@0.0.26
- Updated dependencies [
056609e]:- @tanstack/db@0.0.23
- Updated dependencies [
aeee9a1]:- @tanstack/db@0.0.22
- Updated dependencies [
8e23322]:- @tanstack/db@0.0.21
- Updated dependencies [
f13c11e]:- @tanstack/db@0.0.20
- Updated dependencies [
9f0b0c2]:- @tanstack/db@0.0.19
- Updated dependencies [
7e63d76]:- @tanstack/db@0.0.17
- Updated dependencies [
74c140d]:- @tanstack/db@0.0.14
-
feat: implement Collection Lifecycle Management (#198)
Adds automatic lifecycle management for collections to optimize resource usage.
New Features:
- Added
startSyncoption (defaults tofalse, set totrueto start syncing immediately) - Automatic garbage collection after
gcTime(default 5 minutes) of inactivity - Collection status tracking: "idle" | "loading" | "ready" | "error" | "cleaned-up"
- Manual
preload()andcleanup()methods for lifecycle control
Usage:
const collection = createCollection({ startSync: false, // Enable lazy loading gcTime: 300000, // Cleanup timeout (default: 5 minutes) }) console.log(collection.status) // Current state await collection.preload() // Ensure ready await collection.cleanup() // Manual cleanup
- Added
-
Add createOptimisticAction helper that replaces useOptimisticMutation (#210)
An example of converting a
useOptimisticMutationhook tocreateOptimisticAction. Now all optimistic & server mutation logic are consolidated.-import { useOptimisticMutation } from '@tanstack/react-db' +import { createOptimisticAction } from '@tanstack/react-db' + +// Create the `addTodo` action, passing in your `mutationFn` and `onMutate`. +const addTodo = createOptimisticAction<string>({ + onMutate: (text) => { + // Instantly applies the local optimistic state. + todoCollection.insert({ + id: uuid(), + text, + completed: false + }) + }, + mutationFn: async (text) => { + // Persist the todo to your backend + const response = await fetch('/api/todos', { + method: 'POST', + body: JSON.stringify({ text, completed: false }), + }) + return response.json() + } +}) const Todo = () => { - // Create the `addTodo` mutator, passing in your `mutationFn`. - const addTodo = useOptimisticMutation({ mutationFn }) - const handleClick = () => { - // Triggers the mutationFn - addTodo.mutate(() => - // Instantly applies the local optimistic state. - todoCollection.insert({ - id: uuid(), - text: '🔥 Make app faster', - completed: false - }) - ) + // Triggers the onMutate and then the mutationFn + addTodo('🔥 Make app faster') } return <Button onClick={ handleClick } /> }
-
Updated dependencies [
945868e,0f8a008,57b5f5d]:- @tanstack/db@0.0.13
- Updated dependencies [
f6abe9b]:- @tanstack/db@0.0.12
-
Export
ElectricCollectionUtils& allow passing generic tocreateTransaction(#179) -
Updated dependencies [
66ed58b,c5489ff]:- @tanstack/db@0.0.11
- Updated dependencies [
38d4505]:- @tanstack/db@0.0.10
- Updated dependencies [
2ae0b09]:- @tanstack/db@0.0.9
-
A large refactor of the core
Collectionwith: (#155)- a change to not use Store internally and emit fine grade changes with
subscribeChangesandsubscribeKeyChangesmethods. - changes to the
Collectionapi to be moreMaplike for reads, withget,has,size,entries,keys, andvalues. - renames
config.getIdtoconfig.getKeyfor consistency with theMaplike api.
- a change to not use Store internally and emit fine grade changes with
-
Updated dependencies [
5c538cf,9553366,b4602a0,02adc81,06d8ecc,c50cd51]:- @tanstack/db@0.0.8
-
Expose utilities on collection instances (#161)
Implemented a utility exposure pattern for TanStack DB collections that allows utility functions to be passed as part of collection options and exposes them under a
.utilsnamespace, with full TypeScript typing.- Refactored
createCollectionin packages/db/src/collection.ts to accept options with utilities directly - Added
utilsproperty to CollectionImpl - Added TypeScript types for utility functions and utility records
- Changed Collection from a class to a type, updating all usages to use createCollection() instead
- Updated Electric/Query implementations
- Utilities are now ergonomically accessible under
.utils - Full TypeScript typing is preserved for both collection data and utilities
- API is clean and straightforward - users can call
createCollection(optionsCreator(config))directly - Zero-boilerplate TypeScript pattern that infers utility types automatically
- Refactored
-
Updated dependencies [
8b43ad3]:- @tanstack/db@0.0.7
-
Collections must have a getId function & use an id for update/delete operators (#134)
-
the
keyByquery operator has been removed, keying withing the query pipeline is now automatic (#144) -
Updated dependencies [
1fbb844,338efc2,ee5d026,e7b036c,e4feb0c]:- @tanstack/db@0.0.5
- Updated dependencies [
8ce449e]:- @tanstack/db@0.0.4
- Updated dependencies [
b29420b]:- @tanstack/db@0.0.3
-
Fixed an issue with injecting the optimistic state removal into the reactive live query. (#78)
-
Updated dependencies [
4c82edb]:- @tanstack/db@0.0.2
-
Make transactions first class & move ownership of mutationFn from collections to transactions (#53)
-
Updated dependencies [
b42479c]:- @tanstack/db@0.0.3