Skip to content
Draft
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
61 changes: 61 additions & 0 deletions .github/SSR_RELEASE_PLAN.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
# TanStack DB SSR Release Plan

## Release Goal

Ship TanStack DB SSR as a single coherent story:

- collection-row hydration through `DbClient`
- React provider and descriptor resolution
- derived live query identity with `queryKey` only when necessary
- backwards-compatible dependency arrays with dev warnings until 1.0
- a working TanStack Start demo and E2E proof

## Pre-release Validation

- Run `pnpm --filter @tanstack/db test`.
- Run `pnpm --filter @tanstack/react-db test`.
- Run `pnpm --filter @tanstack/query-db-collection test`.
- Run `pnpm --filter @tanstack/db-sqlite-persistence-core test`.
- Run `pnpm --filter @tanstack/db-example-react-start-ssr-e2e test:e2e`.
- Run `pnpm test:docs`.
- Run `pnpm test:sherif`.
- Run `pnpm build`.

## Demo

- Live URL: https://tanstack-db-ssr-demo.netlify.app/ssr-db
- Deploy `examples/react/start-ssr-e2e` to an SSR-capable host.
- Verify the deployed `/ssr-db` route serves SSR HTML with hydrated rows.
- Verify browser hydration succeeds without console/page errors.
- Verify the streamed collection chunk updates the live query.
- Run `PLAYWRIGHT_BASE_URL=https://tanstack-db-ssr-demo.netlify.app pnpm --filter @tanstack/db-example-react-start-ssr-e2e test:e2e:hosted`.
- Add the live URL to the PR description and release notes.

## Docs

- Publish the [SSR and Hydration guide](../docs/guides/ssr.md).
- Link the guide from overview, quick start, live queries, and React overview.
- Regenerate API reference docs in a dedicated docs-maintenance pass if broad
TypeDoc output churn is acceptable.
- Confirm docs explain when `queryKey` is necessary and when it should be
omitted.
- Confirm docs say dependency arrays warn now and are removed in 1.0.

## Migration Messaging

- Lead with: SSR hydration is collection-row based.
- Emphasize that existing apps keep working.
- State that `createCollection(...)` remains available, but SSR apps should use
`collectionOptions(...)` plus `DbClient`.
- Explain that React dependency arrays are deprecated with a 1.0 removal path.
- Show `queryKey` only for opaque functional query logic or hot render paths.

## Announcement Checklist

- PR description includes high-level summary, migration cheat sheet, and test
commands.
- Release notes include a "No removals in this release" compatibility section.
- Discord announcement links the SSR guide and live demo.
- Example migration diff is available from the Start SSR demo.
- Follow-up issues are filed for non-React framework parity and API reference
generation if they are not part of the shipping PR.
11 changes: 11 additions & 0 deletions .github/workflows/e2e-tests.yml
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,7 @@ jobs:
run: |
pnpm --filter @tanstack/db-ivm build
pnpm --filter @tanstack/db build
pnpm --filter @tanstack/react-db build
pnpm --filter @tanstack/electric-db-collection build
pnpm --filter @tanstack/offline-transactions build
pnpm --filter @tanstack/query-db-collection build
Expand All @@ -68,6 +69,16 @@ jobs:
env:
ELECTRIC_URL: http://localhost:3000

- name: Install Playwright browsers
run: |
cd examples/react/start-ssr-e2e
pnpm exec playwright install --with-deps chromium

- name: Run React Start SSR E2E tests
run: |
cd examples/react/start-ssr-e2e
pnpm test:e2e

- name: Run Node SQLite persisted collection E2E tests
run: |
cd packages/node-db-sqlite-persistence
Expand Down
20 changes: 12 additions & 8 deletions docs/collections/local-only-collection.md
Original file line number Diff line number Diff line change
Expand Up @@ -192,10 +192,12 @@ export const modalStateCollection = createCollection(

// Use in component
function UserProfileModal() {
const { data: modals } = useLiveQuery((q) =>
q.from({ modal: modalStateCollection })
.where(({ modal }) => eq(modal.id, 'user-profile'))
)
const { data: modals } = useLiveQuery({
query: (q) =>
q
.from({ modal: modalStateCollection })
.where(({ modal }) => eq(modal.id, 'user-profile')),
})

const modalState = modals[0]

Expand Down Expand Up @@ -248,10 +250,12 @@ export const formDraftsCollection = createCollection(

// Use in component
function CreatePostForm() {
const { data: drafts } = useLiveQuery((q) =>
q.from({ draft: formDraftsCollection })
.where(({ draft }) => eq(draft.id, 'new-post'))
)
const { data: drafts } = useLiveQuery({
query: (q) =>
q
.from({ draft: formDraftsCollection })
.where(({ draft }) => eq(draft.id, 'new-post')),
})

const currentDraft = drafts[0]

Expand Down
10 changes: 6 additions & 4 deletions docs/collections/local-storage-collection.md
Original file line number Diff line number Diff line change
Expand Up @@ -263,10 +263,12 @@ export const userPreferencesCollection = createCollection(

// Use in component
function SettingsPanel() {
const { data: prefs } = useLiveQuery((q) =>
q.from({ pref: userPreferencesCollection })
.where(({ pref }) => eq(pref.id, 'current-user'))
)
const { data: prefs } = useLiveQuery({
query: (q) =>
q
.from({ pref: userPreferencesCollection })
.where(({ pref }) => eq(pref.id, 'current-user')),
})

const currentPrefs = prefs[0]

Expand Down
16 changes: 12 additions & 4 deletions docs/collections/query-collection.md
Original file line number Diff line number Diff line change
Expand Up @@ -25,13 +25,15 @@ npm install @tanstack/query-db-collection @tanstack/query-core @tanstack/db

```typescript
import { QueryClient } from "@tanstack/query-core"
import { createCollection } from "@tanstack/db"
import { DbClient, collectionOptions } from "@tanstack/db"
import { queryCollectionOptions } from "@tanstack/query-db-collection"

const queryClient = new QueryClient()
const db = new DbClient()

const todosCollection = createCollection(
const todosCollection = collectionOptions(
queryCollectionOptions({
id: "todos",
queryKey: ["todos"],
queryFn: async () => {
const response = await fetch("/api/todos")
Expand All @@ -41,6 +43,8 @@ const todosCollection = createCollection(
getKey: (item) => item.id,
})
)

const todos = db.collection(todosCollection)
```

## Configuration Options
Expand Down Expand Up @@ -70,11 +74,12 @@ If your app already uses TanStack Query's `queryOptions` helper (e.g. from `@tan

```typescript
import { QueryClient } from "@tanstack/query-core"
import { createCollection } from "@tanstack/db"
import { DbClient, collectionOptions } from "@tanstack/db"
import { queryCollectionOptions } from "@tanstack/query-db-collection"
import { queryOptions } from "@tanstack/react-query"

const queryClient = new QueryClient()
const db = new DbClient()

const listOptions = queryOptions({
queryKey: ["todos"],
Expand All @@ -84,14 +89,17 @@ const listOptions = queryOptions({
},
})

const todosCollection = createCollection(
const todosCollection = collectionOptions(
queryCollectionOptions({
id: "todos",
...listOptions,
queryFn: (context) => listOptions.queryFn!(context),
queryClient,
getKey: (item) => item.id,
}),
)

const todos = db.collection(todosCollection)
```

If `queryFn` is missing at runtime, `queryCollectionOptions` throws `QueryFnRequiredError`.
Expand Down
12 changes: 7 additions & 5 deletions docs/collections/trailbase-collection.md
Original file line number Diff line number Diff line change
Expand Up @@ -194,11 +194,13 @@ export const todosCollection = createCollection<SelectTodo, Todo>(

// Use in component
function TodoList() {
const { data: todos } = useLiveQuery((q) =>
q.from({ todo: todosCollection })
.where(({ todo }) => not(todo.completed))
.orderBy(({ todo }) => todo.created_at, 'desc')
)
const { data: todos } = useLiveQuery({
query: (q) =>
q
.from({ todo: todosCollection })
.where(({ todo }) => not(todo.completed))
.orderBy(({ todo }) => todo.created_at, 'desc'),
})

const addTodo = (text: string) => {
todosCollection.insert({
Expand Down
4 changes: 4 additions & 0 deletions docs/config.json
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,10 @@
"label": "Live Queries",
"to": "guides/live-queries"
},
{
"label": "SSR and Hydration",
"to": "guides/ssr"
},
{
"label": "Mutations",
"to": "guides/mutations"
Expand Down
Loading
Loading