A small TypeScript + React state management demo showing how to handle server state and client state with modern tooling:
- TanStack Query — server state (fetching random colours, posting new ones)
- Zustand — client-only UI state (toggling the colour form)
- Bun — runtime, bundler, test runner, and package manager (one tool replaces Node, webpack, babel, Jest, and npm)
It's the same silly colour picker it always was — but the Redux action creators, reducers, thunks, enzyme tests, JavaScript, CJS, webpack, and babel have been replaced by about half the code.
bun install
bun startThen open http://localhost:3000.
bun start builds the client with Bun's bundler in watch mode and starts the Express API server. The server serves both the API routes (/color) and the bundled frontend from the same port — no CORS needed.
client/store.ts— a tiny typed Zustand store holding onlyisColorFormVisibleclient/components/ColorViewer.tsx— TanStackuseQuery<Color>manages theGET /colorcall. Clicking the link callsrefetch(). Loading state comes fromisFetching.client/components/AddColor.tsx— TanStackuseMutationhandles thePOST /colorcall.useRef<HTMLInputElement>for typed DOM access. Form toggle via Zustand'suseStore().server/— a plain Express API with a 2-second artificial delay so you can see the loading states. Three routes:GET /color(random),GET /color/all,POST /color. Callbacks replaced withPromise+async/await.types.ts— sharedColorinterface used across server, client, and tests.tests/— React Testing Library with Bun's native test runner and ahappy-dompreload for DOM globals. No Jest, no enzyme, nojest-dom.
| Command | What it does |
|---|---|
bun start |
Build client + start server, both in watch mode |
bun run build |
Bundle client only (Bun's built-in bundler) |
bun test |
Run tests with Bun's native test runner |
- connect(mapStateToProps, mapDispatchToProps)(ColorViewer)
- dispatch(requestingColor()) → superagent.get(...) → dispatch(receivingColor(data))
+ useQuery<Color>({ queryKey: ['color'], queryFn: fetch('/color').then(r => r.json()) })
- redux store: { color, isWaitingOnApi, isColorFormVisible }
+ tanstack query: { data: Color, isFetching } ← typed server state
+ zustand store: { isColorFormVisible } ← typed client stateThe original Redux + JavaScript version taught action creators, thunks, and connect(). That commit history is still here — scroll back to 64f8179.