Architecture
tempest-react-sdk is a single package with independent layers. You import only
what you use; everything is externalized in the SDK bundle, so your app's bundler
tree-shakes whatever isn't referenced.
This page is the package architecture
Here you learn how the SDK itself is put together — layers, dependencies, subpaths, bundle. If what you want is how to organize your app (layers, folders, where each kind of state lives, file size limits), the page is Frontend app layers, under the Software Design tab.
Import only what you use
There's no penalty for the SDK being large. Each layer (HTTP, auth, query,
forms…) is independent — if you never import createOfflineStore, dexie
never enters your bundle. Start with a single Button and grow from there.
Editable diagram: architecture.drawio (open in draw.io).
Scope: client-side only
This SDK targets client-rendered SPAs with offline capability — service
worker, IndexedDB outbox, install prompt, background sync. It does not
support SSR or React Server Components: no module declares "use client", and
components assume they mount in a browser. Next.js App Router is not a target.
That is a scope choice, not a gap
Covering both worlds would cost at every API (two render paths, hydration, no
window at module top level) and offline-first — the whole reason the package
exists — would come out worse. The typeof window === "undefined" guards in
the hooks exist so they do not throw outside a browser (Node tests, the
service-worker context, build plugins), not to promise server rendering.
Layers
Application foundation
The opinionated base that assembles a whole React app. This is what the
create-tempest-app CLI generates.
| Layer | What it does | Page |
|---|---|---|
Vite (vite/) |
createViteConfig — React plugin + @ → src alias + dev server (subpath /vite). |
Vite & alias |
Router (router/) |
defineRoutes, <AppRouter>, <RouteGuard> + React Router v8 re-exports. |
Routing |
Store (store/) |
createStore, createSelectors (generic Zustand factories). |
State |
App (app/) |
<AppProviders> — composes ErrorBoundary → Query → Theme → i18n in one block. |
Providers |
UI blocks and integrations
| Layer | What it does |
|---|---|
Components (components/) |
70+ UI primitives (Button, Input, Modal, Table, DataTable, Command, Calendar…) with tempest_-prefixed CSS Modules. |
Hooks (hooks/) |
useDebounce, usePagination, useMediaQuery, useKeyboardShortcut, useFocusTrap… |
HTTP (http/) |
createApiClient, parseResponse, uploadWithProgress, retry, usePoll. |
Auth (auth/) |
createAuthStore (Zustand) + AuthGuard + JWT helpers + lazyWithRetry. |
Query (query/) |
QueryProvider, createQueryKeys, time presets. |
| SSE / WebSocket / Push / SW | Real-time transports with reconnect. |
Offline (offline/) |
createOfflineStore (Dexie). |
Forms (forms/) |
useZodForm, zodResolver, FormField, BR masked inputs. |
| Theme / i18n / Logger / Telemetry / Feature Flags | No-flash theme, in-house i18n, leveled logger, injectable adapters. |
Utils (utils/) |
cn, BR format, arrays/objects/guards/functions/promises, strings, numbers, randomId. |
Dependencies
react, react-dom and react-router are peer dependencies —
all three carry React context, and a second copy is not extra bundle weight, it
is a second instance that breaks at runtime. Everything else is a direct
dependency — installed automatically by npm install tempest-react-sdk and
externalized in the bundle (your app's bundler resolves it from node_modules
and tree-shakes).
| Package | Status | Used by |
|---|---|---|
react, react-dom |
Peer (required) | Everything |
react-router (^7 \|\| ^8) |
Peer (required) | AppRouter, defineRoutes, RouteGuard, re-exports |
zustand |
Direct dep | createStore, createSelectors, createAuthStore |
@tanstack/react-query |
Direct dep | QueryProvider, createQueryKeys, AppProviders |
zod |
Direct dep | parseResponse, validateForm, zodResolver, useZodForm |
react-hook-form |
Direct dep | useZodForm, FormField, masked inputs |
dexie |
Direct dep | createOfflineStore |
lucide-react |
Direct dep | Icons (leftIcon/rightIcon) |
vite, @vitejs/plugin-react |
Optional peer | createViteConfig (subpath tempest-react-sdk/vite) — already in any Vite app |
Why react-router is a peer, not a direct dep
It holds React context. A copy nested under
tempest-react-sdk/node_modules is a different <Router> context than
the one your app renders, so any SDK hook reaching for it throws
useNavigate() may be used only in the context of a <Router> — a runtime
crash, not a size regression. Same reason react itself is a peer, and it is
the one exception to the "everything else is a direct dep" rule. The
^7 || ^8 range lets an app on either major install a single copy: the
re-exported surface is identical across both, and both ship the DOM bindings
inside react-router itself (there is no separate react-router-dom).
The rest stays a direct dep
zustand, zod, dexie, react-hook-form, @tanstack/react-query and
lucide-react are direct dependencies — npm install tempest-react-sdk
pulls them all in, with nothing for you to list by hand. Two copies of those
cost bytes, not correctness.
Adapters for external SDKs (Sentry, PostHog, GrowthBook, LaunchDarkly) are not declared — the caller injects the instance into the factory.
Subpaths
| Import | Contents |
|---|---|
tempest-react-sdk |
Main barrel (components, hooks, foundation…). |
tempest-react-sdk/styles.css |
--tempest-* tokens + reset + CSS Modules. |
tempest-react-sdk/vite |
createViteConfig (Node-only, for vite.config.ts). |
tempest-react-sdk/testing |
createMockHandlers (MSW test helpers). |
tempest-react-sdk/icons |
Icon by slug + static registry (Icons). |
Bundle
Vite library mode → ESM (tempest-react-sdk.js) + CJS (.cjs) + rolled-up
.d.ts + styles.css (CSS Modules in a single file, cssCodeSplit: false).
Budget monitored by size-limit in CI.
Recap
- One package, independent layers; you import only what you use and the bundler tree-shakes the rest.
- Only
react+react-domare peers; the other libs are direct deps installed alongside. - Subpaths: the main barrel,
…/styles.css,…/vite(Node-only),…/testingand…/icons(icon by slug). - The app foundation (Vite · Router ·
Store · Providers) is what
create-tempest-appassembles for you.