<!--
Sitemap:
- [rxfy](/index): Typed, normalized, reactive state — built on RxJS
- [Comparison](/comparison): rxfy versus Redux Toolkit, MobX, Jotai, TanStack Query, and TanStack DB
- [Inspired by](/inspired-by): the libraries and ideas rxfy grew out of
- [Agent Skills](/agent-skills): Accurate rxfy context for AI coding assistants
- [Examples](/examples): Runnable apps, from client-only to fully synced
- [Changelog](/changelog)
- [Getting Started](/getting-started)
- [Create Store](/getting-started/create-store): Normalized reactive state in a client-only app
- [Add SSR](/getting-started/add-ssr): Render the first paint on the server, hydrate with no refetch
- [Add Sync Client](/getting-started/add-sync-client): The full stack: the server publishes, the client syncs
- [Core Concepts](/core-concepts): The ideas rxfy is built on
- [Observables](/core-concepts/observables): A value that changes over time, that you can subscribe to
- [Normalization](/core-concepts/normalization): Store each entity once, reference it by id
- [Late Unwrapping](/core-concepts/late-unwrapping): Unwrap async state at the leaf, not the trunk
- [Server-Side Rendering](/core-concepts/ssr): Dehydrate on the server, hydrate with no refetch
- [rxfy](/rxfy): The core package: atoms, lenses, models, and states
- [createModel](/rxfy/create-model): Typed entities in normalized storage
- [defineState](/rxfy/define-state): Typed, normalized state shapes with fetching and mutations
- [createAtom](/rxfy/create-atom): A reactive cell with synchronous get, set, and modify
- [createLens](/rxfy/create-lens): A two-way view into part of an Atom
- [React Bindings](/react): Hooks and helpers for using rxfy in React
- [useStateData](/react/use-state-data): Fetch, normalize, and subscribe to a query
- [useStatePagedData](/react/use-state-paged-data): Paginated and infinite-scroll lists
- [useModelStore](/react/use-model-store): Subscribe to one normalized entity by id
- [useAtom](/react/use-atom): Two-way binding for any IAtom
- [Pending](/react/pending): Render pending, rejected, and fulfilled UI for any observable
- [usePending](/react/use-pending): The status value behind Pending
- [useObservable](/react/use-observable): Bind a raw Observable to React
- [Sync Client in React](/react/sync-client): StoreProvider, useSyncClient, and update handles
- [rxfy-client](/framework/client): The framework-agnostic browser sync runtime
- [createSyncClient](/framework/client/create-sync-client): Connect a transport and drive the sync loop
- [readSsrGrants](/framework/client/read-ssr-grants): Lift SSR-embedded channel grants
- [rxfy-server](/framework/server): Bind Drizzle tables, write, and publish sync updates
- [defineResource](/framework/server/define-resource): Tie a Drizzle table to an rxfy model
- [createSync](/framework/server/create-server): Wire a storage adapter, hub, and secret into a Live object
- [createInMemoryHub](/framework/server/hub): The socket-keyed pub/sub backbone
- [Writes](/framework/server/writes): sync.create / sync.update / sync.delete and touch
- [Storage adapters](/framework/server/storage-adapters): Persist writes with Drizzle or in memory
- [Sync messages](/framework/server/messages): What travels between server and client
- [Grants](/framework/server/grants): The server signs what it serves; the client subscribes with the token
- [rxfy-ws](/framework/ws): The default WebSocket transport
- [createWsServer](/framework/ws/server): Attach a Hub to WebSocket connections
- [createWsClient](/framework/ws/client): The browser transport with reconnect and replay
- [Custom transports](/framework/ws/custom-transport): Bring your own ClientTransport
- [Guides](/guides): Task-focused walkthroughs of common rxfy patterns
- [Pagination and infinite scroll](/guides/pagination): Load and append pages into one normalized list
-->

# Examples \[Runnable apps, from client-only to fully synced]

Runnable example apps in the [rxfy repository](https://github.com/vanya2h/rxfy/tree/main/examples), arranged the way the docs teach rxfy: start with SSR, then turn on sync.

:::note
The examples are workspace apps — their `workspace:*` dependencies only resolve inside a
clone of the [rxfy repository](https://github.com/vanya2h/rxfy), and the `pnpm --filter … dev`
commands below run from its repo root. They will do nothing in your own project. To start a
standalone app, use [`pnpm create rxfy-app`](/getting-started).
:::

## Tier 1 · Server-side rendering

### vite-ssr-pagination

**[examples/vite-ssr-pagination](https://github.com/vanya2h/rxfy/tree/main/examples/vite-ssr-pagination)**

A **truly infinite** users directory paged one page at a time — with a switch between a "Load more" button and an `IntersectionObserver` infinite-scroll sentinel. Built on `useStatePagedData(userModel, …)`: page 1 is server-rendered through `useStateData`, and `loadMore()` appends later pages client-side with O(page-size) work (only new entities written), so `data$`'s `string[]` of ids extends one normalized list while row data lives in the `userModel` store. Rows are generated on demand with `@faker-js/faker` (server-only, seeded per index), so the list never ends. Built on the Vite `ssr-react-streaming-ts` template: the shell streams via `renderToPipeableStream` + `onShellReady`, and a single `hydrationScript(dehydrate(registry))` is injected at the end of the stream.

```bash
pnpm --filter rxfy-example-ssr-pagination dev
# open http://localhost:5176 — scroll to keep loading, forever
```

Companion guide: [Pagination and infinite scroll](/guides/pagination)

### next-blog

**[examples/next-blog](https://github.com/vanya2h/rxfy/tree/main/examples/next-blog)**

A blog app using Next.js App Router with streaming SSR. Demonstrates branded Zod types (`UserId`, `PostId`, `CommentId`), multiple related models (`User`, `Post`, `Comment`), and `<HydrationStream />` from `rxfy-react/next` for progressive hydration as Suspense boundaries resolve.

```bash
pnpm --filter rxfy-example-next-blog dev
```

### rr7-blog

**[examples/rr7-blog](https://github.com/vanya2h/rxfy/tree/main/examples/rr7-blog)**

The same blog domain as `next-blog`, but on React Router 7 (framework mode) with buffered SSR — a direct side-by-side of two SSR strategies. Here rxfy is the **single data layer**: a customized `entry.server` creates a per-request registry, renders with `renderToPipeableStream` + `onAllReady`, and injects `hydrationScript(dehydrate(registry))` before `</body>`; the client drains it for a zero-refetch first paint. React Router loaders are reserved for **routing only** — an index redirect and URL-param validation — never data fetching.

```bash
pnpm --filter rxfy-example-rr7-blog dev
```

### waku-blog

**[examples/waku-blog](https://github.com/vanya2h/rxfy/tree/main/examples/waku-blog)**

The same blog domain as `next-blog` and `rr7-blog`, on [Waku](https://waku.gg) (the minimal, RSC-based React framework) — a static home (`getConfig { render: "static" }`) and a per-request dynamic post page (`render: "dynamic" }`). Waku exposes no script-injection seam (no `useServerInsertedHTML`, no custom `entry.server`), so rxfy hydrates the RSC way: each page **prefetches before render** in its Server Component, `dehydrate`s a per-request registry, and passes the JSON snapshot to a small `<HydrateSnapshot />` client component that merges it into the persistent layout-owned `StoreProvider` — zero client fetch on first paint, with the store persisting across client navigations. The `prefetch` helper is example-local, built only from public rxfy exports (`normalizeResult`, `stableStringify`, `createFulfilled`, `dehydrate`).

```bash
pnpm --filter rxfy-example-waku-blog dev
```

## Tier 2 · Sync (the framework)

### vite-blog

**[examples/vite-blog](https://github.com/vanya2h/rxfy/tree/main/examples/vite-blog)**

A sync blog: server-rendered first paint, then real-time sync over WebSockets. Edits apply instantly across tabs (a `patch` on the entity topic); new posts/comments and deletes surface a "click to refresh" badge (a `stale` on the state channel). Built with Vite SSR · Hono · Hono WebSocket · PGlite (in-memory Postgres) · Drizzle · `rxfy-server` · `rxfy-ws` · `rxfy-client`. SSR signs channel **grants** for exactly what the page rendered; the client subscribes with them over the WebSocket.

```bash
pnpm --filter vite-blog dev
# open in two tabs — edit a post in one, watch it change live in the other
```

This is the exact stack that [Add Sync Client](/getting-started/add-sync-client) builds by hand,
scaled up to three related entity types.

### vite-kanban

**[examples/vite-kanban](https://github.com/vanya2h/rxfy/tree/main/examples/vite-kanban)**

A live kanban board on the same sync stack (Vite SSR · Hono · Hono WebSocket · PGlite · Drizzle · `rxfy-server` · `rxfy-ws` · `rxfy-client`), built the idiomatic way: **the query holds the board's structure as ordered id arrays per column; each card component unwraps its own entity from the normalized store.** It sharpens the `patch`/`stale` split from the structure side — moving, reordering, creating, or deleting a card changes *structure* and emits a `stale` on the `board` channel (stale-while-revalidate refetch, no loading flash), while editing a card's title/description is an in-place `patch` on the `card:<id>` topic that re-renders just that card with no refetch. Drags are optimistic locally and reconciled by the refetch; card ordering uses `fractional-indexing` keys so drops between neighbors stay stable.

```bash
pnpm --filter vite-kanban dev
# open in two tabs — drag a card in one, watch it move live in the other
```
