Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
42 changes: 42 additions & 0 deletions docs/community/resources.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
# Community Resources

This page contains a curated list of community-created packages, tools, and resources that extend or complement TanStack DB.

## Community Packages

### Dexie.js Integration (Unofficial)
- **[tanstack-dexie-db-collection](https://github.com/yourusername/tanstack-dexie-db-collection)** - Community-maintained Dexie.js adapter for TanStack DB
- Local persistence using [Dexie.js](https://dexie.org) (IndexedDB wrapper)
- Lightweight integration for browser-based storage
- Install: `npm install tanstack-dexie-db-collection`

### Contributing Your Package

Have you created a collection adapter or integration? We'd love to feature it here! [Submit a PR](https://github.com/TanStack/db/pulls) to add your package.

## Examples & Templates

### Starter Templates
*Share your starter templates and boilerplates here*

### Example Applications
*Community-built example apps showcasing TanStack DB features*

## Learning Resources

### Tutorials & Guides
*Community tutorials and blog posts about TanStack DB*

### Videos & Courses
*Video tutorials and online courses*

## Contributing

We welcome contributions to this community resources page! If you've created something useful for the TanStack DB ecosystem:

1. Fork the repository
2. Add your resource to the appropriate section
3. Include a brief description and link
4. Submit a pull request

Please ensure all submissions are relevant to TanStack DB and provide value to the community.
9 changes: 9 additions & 0 deletions docs/config.json
Original file line number Diff line number Diff line change
Expand Up @@ -104,6 +104,15 @@
}
]
},
{
"label": "Community",
"children": [
{
"label": "Resources & Packages",
"to": "community/resources"
}
]
},
{
"label": "API Reference",
"children": [
Expand Down
173 changes: 93 additions & 80 deletions docs/overview.md
Original file line number Diff line number Diff line change
Expand Up @@ -186,8 +186,8 @@ const todoCollection = createCollection(
queryCollectionOptions({
queryKey: ["todoItems"],
queryFn: async () => {
const response = await fetch("/api/todos");
return response.json();
const response = await fetch("/api/todos")
return response.json()
},
getKey: (item) => item.id,
schema: todoSchema, // any standard schema
Expand Down Expand Up @@ -301,7 +301,6 @@ This collection requires the following TrailBase-specific options:

A new collections doesn't start syncing until you call `collection.preload()` or you query it.


#### `RxDBCollection`

[RxDB](https://rxdb.info) is a client-side database for JavaScript apps with replication, conflict resolution, and offline-first features.
Expand Down Expand Up @@ -335,7 +334,7 @@ await db.addCollections({
export const todoCollection = createCollection(
rxdbCollectionOptions({
rxCollection: db.todos,
startSync: true
startSync: true,
})
)
```
Expand Down Expand Up @@ -614,11 +613,11 @@ const addTodo = createOptimisticAction<string>({
body: JSON.stringify({ text, completed: false }),
})
const result = await response.json()

// IMPORTANT: Ensure server writes have synced back before returning
// This ensures the optimistic state can be safely discarded
await todoCollection.utils.refetch()

return result
},
})
Expand Down Expand Up @@ -671,47 +670,54 @@ When multiple mutations operate on the same item within a transaction, TanStack

The merging behavior follows a truth table based on the mutation types:

| Existing → New | Result | Description |
|---|---|---|
| **insert + update** | `insert` | Keeps insert type, merges changes, empty original |
| **insert + delete** | *removed* | Mutations cancel each other out |
| **update + delete** | `delete` | Delete dominates |
| **update + update** | `update` | Union changes, keep first original |
| **same type** | *latest* | Replace with most recent mutation |
| Existing → New | Result | Description |
| ------------------- | --------- | ------------------------------------------------- |
| **insert + update** | `insert` | Keeps insert type, merges changes, empty original |
| **insert + delete** | _removed_ | Mutations cancel each other out |
| **update + delete** | `delete` | Delete dominates |
| **update + update** | `update` | Union changes, keep first original |
| **same type** | _latest_ | Replace with most recent mutation |

#### Examples

**Insert followed by update:**

```ts
const tx = createTransaction({ autoCommit: false, mutationFn })

// Insert a new todo
tx.mutate(() => todoCollection.insert({
id: '1',
text: 'Buy groceries',
completed: false
}))
tx.mutate(() =>
todoCollection.insert({
id: "1",
text: "Buy groceries",
completed: false,
})
)

// Update the same todo
tx.mutate(() => todoCollection.update('1', (draft) => {
draft.text = 'Buy organic groceries'
draft.priority = 'high'
}))
tx.mutate(() =>
todoCollection.update("1", (draft) => {
draft.text = "Buy organic groceries"
draft.priority = "high"
})
)

// Result: Single insert mutation with merged data
// { id: '1', text: 'Buy organic groceries', completed: false, priority: 'high' }
```

**Insert followed by delete:**

```ts
// Insert then delete cancels out - no mutations sent to server
tx.mutate(() => todoCollection.insert({ id: '1', text: 'Temp todo' }))
tx.mutate(() => todoCollection.delete('1'))
tx.mutate(() => todoCollection.insert({ id: "1", text: "Temp todo" }))
tx.mutate(() => todoCollection.delete("1"))

// Result: No mutations (they cancel each other out)
```

This intelligent merging ensures that:

- **Network efficiency**: Fewer mutations sent to the server
- **User intent preservation**: Final state matches what user expects
- **Optimistic UI consistency**: Local state always reflects user actions
Expand Down Expand Up @@ -899,32 +905,36 @@ import { queryCollectionOptions } from "@tanstack/query-db-collection"

// Load data into collections using TanStack Query.
// It's common to define these in a `collections` module.
const todoCollection = createCollection(queryCollectionOptions({
queryKey: ["todos"],
queryFn: async () => fetch("/api/todos"),
getKey: (item) => item.id,
schema: todoSchema, // any standard schema
onInsert: async ({ transaction }) => {
const { changes: newTodo } = transaction.mutations[0]

// Handle the local write by sending it to your API.
await api.todos.create(newTodo)
}
// also add onUpdate, onDelete as needed.
}))
const listCollection = createCollection(queryCollectionOptions({
queryKey: ["todo-lists"],
queryFn: async () => fetch("/api/todo-lists"),
getKey: (item) => item.id,
schema: todoListSchema,
onInsert: async ({ transaction }) => {
const { changes: newTodo } = transaction.mutations[0]

// Handle the local write by sending it to your API.
await api.todoLists.create(newTodo)
}
// also add onUpdate, onDelete as needed.
}))
const todoCollection = createCollection(
queryCollectionOptions({
queryKey: ["todos"],
queryFn: async () => fetch("/api/todos"),
getKey: (item) => item.id,
schema: todoSchema, // any standard schema
onInsert: async ({ transaction }) => {
const { changes: newTodo } = transaction.mutations[0]

// Handle the local write by sending it to your API.
await api.todos.create(newTodo)
},
// also add onUpdate, onDelete as needed.
})
)
const listCollection = createCollection(
queryCollectionOptions({
queryKey: ["todo-lists"],
queryFn: async () => fetch("/api/todo-lists"),
getKey: (item) => item.id,
schema: todoListSchema,
onInsert: async ({ transaction }) => {
const { changes: newTodo } = transaction.mutations[0]

// Handle the local write by sending it to your API.
await api.todoLists.create(newTodo)
},
// also add onUpdate, onDelete as needed.
})
)

const Todos = () => {
// Read the data using live queries. Here we show a live
Expand All @@ -935,19 +945,18 @@ const Todos = () => {
.join(
{ list: listCollection },
({ todo, list }) => eq(list.id, todo.list_id),
'inner'
"inner"
)
.where(({ list }) => eq(list.active, true))
.select(({ todo, list }) => ({
id: todo.id,
text: todo.text,
status: todo.status,
listName: list.name
listName: list.name,
}))
)

// ...

}
```

Expand All @@ -960,37 +969,41 @@ One of the most powerful ways of using TanStack DB is with a sync engine, for a
Here, we illustrate this pattern using [ElectricSQL](https://electric-sql.com) as the sync engine.

```tsx
import type { Collection } from '@tanstack/db'
import type { MutationFn, PendingMutation, createCollection } from '@tanstack/react-db'
import { electricCollectionOptions } from '@tanstack/electric-db-collection'

export const todoCollection = createCollection(electricCollectionOptions({
id: 'todos',
schema: todoSchema,
// Electric syncs data using "shapes". These are filtered views
// on database tables that Electric keeps in sync for you.
shapeOptions: {
url: 'https://api.electric-sql.cloud/v1/shape',
params: {
table: 'todos'
}
},
getKey: (item) => item.id,
schema: todoSchema,
onInsert: async ({ transaction }) => {
const response = await api.todos.create(transaction.mutations[0].modified)
import type { Collection } from "@tanstack/db"
import type {
MutationFn,
PendingMutation,
createCollection,
} from "@tanstack/react-db"
import { electricCollectionOptions } from "@tanstack/electric-db-collection"

return { txid: response.txid}
}
// You can also implement onUpdate, onDelete as needed.
}))
export const todoCollection = createCollection(
electricCollectionOptions({
id: "todos",
schema: todoSchema,
// Electric syncs data using "shapes". These are filtered views
// on database tables that Electric keeps in sync for you.
shapeOptions: {
url: "https://api.electric-sql.cloud/v1/shape",
params: {
table: "todos",
},
},
getKey: (item) => item.id,
schema: todoSchema,
onInsert: async ({ transaction }) => {
const response = await api.todos.create(transaction.mutations[0].modified)

return { txid: response.txid }
},
// You can also implement onUpdate, onDelete as needed.
})
)

const AddTodo = () => {
return (
<Button
onClick={() =>
todoCollection.insert({ text: "🔥 Make app faster" })
}
onClick={() => todoCollection.insert({ text: "🔥 Make app faster" })}
/>
)
}
Expand All @@ -1009,7 +1022,7 @@ npm install react-native-random-uuid
Then import it at the entry point of your React Native app (e.g., in your `App.js` or `index.js`):

```javascript
import 'react-native-random-uuid'
import "react-native-random-uuid"
```

This polyfill provides the `crypto.randomUUID()` function that TanStack DB uses internally for generating unique identifiers.
Expand Down