Skip to content

Commit cc46951

Browse files
kevin-dpclaudeautofix-ci[bot]
authored
fix: clean up offline-transactions example (#1438)
* Improve readme * Fix build errors * Remove stuff we don't need from tanstack start * Update lockfile after removing tailwind-merge Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * ci: apply automated fixes * Fix API routes to use createFileRoute with server.handlers The API routes were using createServerFileRoute which doesn't exist in @tanstack/react-start 1.159.5. Convert to the correct API: createFileRoute with server.handlers. This fixes the 404s on /api/todos endpoints. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com> Co-authored-by: autofix-ci[bot] <114827586+autofix-ci[bot]@users.noreply.github.com>
1 parent f4a9bd2 commit cc46951

15 files changed

Lines changed: 204 additions & 464 deletions

File tree

Lines changed: 17 additions & 55 deletions
Original file line numberDiff line numberDiff line change
@@ -1,72 +1,34 @@
1-
# Welcome to TanStack.com!
1+
# Offline Transactions Example
22

3-
This site is built with TanStack Router!
3+
A todo app demonstrating `@tanstack/offline-transactions` with three different browser storage backends:
44

5-
- [TanStack Router Docs](https://tanstack.com/router)
5+
- **IndexedDB** — persistent structured storage via `IndexedDBAdapter`
6+
- **localStorage** — simple key-value fallback via `LocalStorageAdapter`
7+
- **wa-sqlite OPFS** — full SQLite database in the browser via `@tanstack/browser-db-sqlite-persistence`
68

7-
It's deployed automagically with Netlify!
9+
The app uses TanStack Start in SPA mode with an in-memory server-side todo store. The server simulates network delays and random failures to demonstrate offline resilience.
810

9-
- [Netlify](https://netlify.com/)
11+
## How to run
1012

11-
## Development
12-
13-
From your terminal:
13+
From the root of the repository:
1414

1515
```sh
1616
pnpm install
17-
pnpm dev
18-
```
19-
20-
This starts your app in development mode, rebuilding assets on file changes.
21-
22-
## Editing and previewing the docs of TanStack projects locally
23-
24-
The documentations for all TanStack projects except for `React Charts` are hosted on [https://tanstack.com](https://tanstack.com), powered by this TanStack Router app.
25-
In production, the markdown doc pages are fetched from the GitHub repos of the projects, but in development they are read from the local file system.
26-
27-
Follow these steps if you want to edit the doc pages of a project (in these steps we'll assume it's [`TanStack/form`](https://github.com/tanstack/form)) and preview them locally :
28-
29-
1. Create a new directory called `tanstack`.
30-
31-
```sh
32-
mkdir tanstack
33-
```
34-
35-
2. Enter the directory and clone this repo and the repo of the project there.
36-
37-
```sh
38-
cd tanstack
39-
git clone git@github.com:TanStack/tanstack.com.git
40-
git clone git@github.com:TanStack/form.git
17+
pnpm build
4118
```
4219

43-
> [!NOTE]
44-
> Your `tanstack` directory should look like this:
45-
>
46-
> ```
47-
> tanstack/
48-
> |
49-
> +-- form/
50-
> |
51-
> +-- tanstack.com/
52-
> ```
53-
54-
> [!WARNING]
55-
> Make sure the name of the directory in your local file system matches the name of the project's repo. For example, `tanstack/form` must be cloned into `form` (this is the default) instead of `some-other-name`, because that way, the doc pages won't be found.
56-
57-
3. Enter the `tanstack/tanstack.com` directory, install the dependencies and run the app in dev mode:
20+
Then from this directory:
5821

5922
```sh
60-
cd tanstack.com
61-
pnpm i
62-
# The app will run on https://localhost:3000 by default
6323
pnpm dev
6424
```
6525

66-
4. Now you can visit http://localhost:3000/form/latest/docs/overview in the browser and see the changes you make in `tanstack/form/docs`.
26+
The app runs at http://localhost:3000.
6727

68-
> [!NOTE]
69-
> The updated pages need to be manually reloaded in the browser.
28+
## What it demonstrates
7029

71-
> [!WARNING]
72-
> You will need to update the `docs/config.json` file (in the project's repo) if you add a new doc page!
30+
- **Outbox pattern** — mutations are persisted locally before syncing to the server
31+
- **Automatic retry** — failed operations retry with exponential backoff when connectivity returns
32+
- **Multi-tab coordination** — leader election ensures only one tab manages offline storage
33+
- **Optimistic updates** — UI updates immediately while mutations sync in the background
34+
- **Collection-level persistence** (wa-sqlite route) — data stored in a real SQLite database in the browser via OPFS, surviving page reloads without server sync

examples/react/offline-transactions/package.json

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -5,8 +5,7 @@
55
"type": "module",
66
"scripts": {
77
"dev": "vite dev",
8-
"build": "vite build && tsc --noEmit",
9-
"start": "node .output/server/index.mjs"
8+
"build": "vite build && tsc --noEmit"
109
},
1110
"dependencies": {
1211
"@tanstack/browser-db-sqlite-persistence": "^0.1.6",
@@ -20,7 +19,6 @@
2019
"@tanstack/react-start": "^1.159.5",
2120
"react": "^19.2.4",
2221
"react-dom": "^19.2.4",
23-
"tailwind-merge": "^2.6.1",
2422
"zod": "^3.25.76"
2523
},
2624
"devDependencies": {

examples/react/offline-transactions/src/components/DefaultCatchBoundary.tsx

Lines changed: 0 additions & 53 deletions
This file was deleted.

examples/react/offline-transactions/src/components/NotFound.tsx

Lines changed: 0 additions & 25 deletions
This file was deleted.

examples/react/offline-transactions/src/routeTree.gen.ts

Lines changed: 68 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,8 @@ import { Route as WaSqliteRouteImport } from './routes/wa-sqlite'
1313
import { Route as LocalstorageRouteImport } from './routes/localstorage'
1414
import { Route as IndexeddbRouteImport } from './routes/indexeddb'
1515
import { Route as IndexRouteImport } from './routes/index'
16+
import { Route as ApiTodosRouteImport } from './routes/api/todos'
17+
import { Route as ApiTodosTodoIdRouteImport } from './routes/api/todos.$todoId'
1618

1719
const WaSqliteRoute = WaSqliteRouteImport.update({
1820
id: '/wa-sqlite',
@@ -34,39 +36,75 @@ const IndexRoute = IndexRouteImport.update({
3436
path: '/',
3537
getParentRoute: () => rootRouteImport,
3638
} as any)
39+
const ApiTodosRoute = ApiTodosRouteImport.update({
40+
id: '/api/todos',
41+
path: '/api/todos',
42+
getParentRoute: () => rootRouteImport,
43+
} as any)
44+
const ApiTodosTodoIdRoute = ApiTodosTodoIdRouteImport.update({
45+
id: '/$todoId',
46+
path: '/$todoId',
47+
getParentRoute: () => ApiTodosRoute,
48+
} as any)
3749

3850
export interface FileRoutesByFullPath {
3951
'/': typeof IndexRoute
4052
'/indexeddb': typeof IndexeddbRoute
4153
'/localstorage': typeof LocalstorageRoute
4254
'/wa-sqlite': typeof WaSqliteRoute
55+
'/api/todos': typeof ApiTodosRouteWithChildren
56+
'/api/todos/$todoId': typeof ApiTodosTodoIdRoute
4357
}
4458
export interface FileRoutesByTo {
4559
'/': typeof IndexRoute
4660
'/indexeddb': typeof IndexeddbRoute
4761
'/localstorage': typeof LocalstorageRoute
4862
'/wa-sqlite': typeof WaSqliteRoute
63+
'/api/todos': typeof ApiTodosRouteWithChildren
64+
'/api/todos/$todoId': typeof ApiTodosTodoIdRoute
4965
}
5066
export interface FileRoutesById {
5167
__root__: typeof rootRouteImport
5268
'/': typeof IndexRoute
5369
'/indexeddb': typeof IndexeddbRoute
5470
'/localstorage': typeof LocalstorageRoute
5571
'/wa-sqlite': typeof WaSqliteRoute
72+
'/api/todos': typeof ApiTodosRouteWithChildren
73+
'/api/todos/$todoId': typeof ApiTodosTodoIdRoute
5674
}
5775
export interface FileRouteTypes {
5876
fileRoutesByFullPath: FileRoutesByFullPath
59-
fullPaths: '/' | '/indexeddb' | '/localstorage' | '/wa-sqlite'
77+
fullPaths:
78+
| '/'
79+
| '/indexeddb'
80+
| '/localstorage'
81+
| '/wa-sqlite'
82+
| '/api/todos'
83+
| '/api/todos/$todoId'
6084
fileRoutesByTo: FileRoutesByTo
61-
to: '/' | '/indexeddb' | '/localstorage' | '/wa-sqlite'
62-
id: '__root__' | '/' | '/indexeddb' | '/localstorage' | '/wa-sqlite'
85+
to:
86+
| '/'
87+
| '/indexeddb'
88+
| '/localstorage'
89+
| '/wa-sqlite'
90+
| '/api/todos'
91+
| '/api/todos/$todoId'
92+
id:
93+
| '__root__'
94+
| '/'
95+
| '/indexeddb'
96+
| '/localstorage'
97+
| '/wa-sqlite'
98+
| '/api/todos'
99+
| '/api/todos/$todoId'
63100
fileRoutesById: FileRoutesById
64101
}
65102
export interface RootRouteChildren {
66103
IndexRoute: typeof IndexRoute
67104
IndexeddbRoute: typeof IndexeddbRoute
68105
LocalstorageRoute: typeof LocalstorageRoute
69106
WaSqliteRoute: typeof WaSqliteRoute
107+
ApiTodosRoute: typeof ApiTodosRouteWithChildren
70108
}
71109

72110
declare module '@tanstack/react-router' {
@@ -99,14 +137,41 @@ declare module '@tanstack/react-router' {
99137
preLoaderRoute: typeof IndexRouteImport
100138
parentRoute: typeof rootRouteImport
101139
}
140+
'/api/todos': {
141+
id: '/api/todos'
142+
path: '/api/todos'
143+
fullPath: '/api/todos'
144+
preLoaderRoute: typeof ApiTodosRouteImport
145+
parentRoute: typeof rootRouteImport
146+
}
147+
'/api/todos/$todoId': {
148+
id: '/api/todos/$todoId'
149+
path: '/$todoId'
150+
fullPath: '/api/todos/$todoId'
151+
preLoaderRoute: typeof ApiTodosTodoIdRouteImport
152+
parentRoute: typeof ApiTodosRoute
153+
}
102154
}
103155
}
104156

157+
interface ApiTodosRouteChildren {
158+
ApiTodosTodoIdRoute: typeof ApiTodosTodoIdRoute
159+
}
160+
161+
const ApiTodosRouteChildren: ApiTodosRouteChildren = {
162+
ApiTodosTodoIdRoute: ApiTodosTodoIdRoute,
163+
}
164+
165+
const ApiTodosRouteWithChildren = ApiTodosRoute._addFileChildren(
166+
ApiTodosRouteChildren,
167+
)
168+
105169
const rootRouteChildren: RootRouteChildren = {
106170
IndexRoute: IndexRoute,
107171
IndexeddbRoute: IndexeddbRoute,
108172
LocalstorageRoute: LocalstorageRoute,
109173
WaSqliteRoute: WaSqliteRoute,
174+
ApiTodosRoute: ApiTodosRouteWithChildren,
110175
}
111176
export const routeTree = rootRouteImport
112177
._addFileChildren(rootRouteChildren)

examples/react/offline-transactions/src/router.tsx

Lines changed: 0 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,10 @@
11
import { createRouter as createTanStackRouter } from '@tanstack/react-router'
22
import { routeTree } from './routeTree.gen'
3-
import { DefaultCatchBoundary } from './components/DefaultCatchBoundary'
4-
import { NotFound } from './components/NotFound'
53

64
export function createRouter() {
75
const router = createTanStackRouter({
86
routeTree,
97
defaultPreload: `intent`,
10-
defaultErrorComponent: DefaultCatchBoundary,
11-
defaultNotFoundComponent: () => <NotFound />,
128
scrollRestoration: true,
139
})
1410

examples/react/offline-transactions/src/routes/__root.tsx

Lines changed: 1 addition & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -8,10 +8,7 @@ import {
88
import { TanStackRouterDevtools } from '@tanstack/react-router-devtools'
99
import { QueryClientProvider } from '@tanstack/react-query'
1010
import * as React from 'react'
11-
import { DefaultCatchBoundary } from '~/components/DefaultCatchBoundary'
12-
import { NotFound } from '~/components/NotFound'
1311
import appCss from '~/styles/app.css?url'
14-
import { seo } from '~/utils/seo'
1512
import { queryClient } from '~/utils/queryClient'
1613

1714
export const Route = createRootRoute({
@@ -24,42 +21,9 @@ export const Route = createRootRoute({
2421
name: `viewport`,
2522
content: `width=device-width, initial-scale=1`,
2623
},
27-
...seo({
28-
title: `TanStack Start | Type-Safe, Client-First, Full-Stack React Framework`,
29-
description: `TanStack Start is a type-safe, client-first, full-stack React framework. `,
30-
}),
31-
],
32-
links: [
33-
{ rel: `stylesheet`, href: appCss },
34-
{
35-
rel: `apple-touch-icon`,
36-
sizes: `180x180`,
37-
href: `/apple-touch-icon.png`,
38-
},
39-
{
40-
rel: `icon`,
41-
type: `image/png`,
42-
sizes: `32x32`,
43-
href: `/favicon-32x32.png`,
44-
},
45-
{
46-
rel: `icon`,
47-
type: `image/png`,
48-
sizes: `16x16`,
49-
href: `/favicon-16x16.png`,
50-
},
51-
{ rel: `manifest`, href: `/site.webmanifest`, color: `#fffff` },
52-
{ rel: `icon`, href: `/favicon.ico` },
53-
],
54-
scripts: [
55-
{
56-
src: `/customScript.js`,
57-
type: `text/javascript`,
58-
},
5924
],
25+
links: [{ rel: `stylesheet`, href: appCss }],
6026
}),
61-
errorComponent: DefaultCatchBoundary,
62-
notFoundComponent: () => <NotFound />,
6327
shellComponent: RootDocument,
6428
})
6529

0 commit comments

Comments
 (0)