SQLRooms is a pnpm monorepo of TypeScript packages for building browser-based analytics apps powered by DuckDB.
- Node.js
>=22 - Package manager:
pnpm
| Command | Purpose |
|---|---|
pnpm install |
Install all dependencies |
pnpm build |
Build all packages via Turbo |
pnpm test |
Run Jest tests |
pnpm typecheck |
TypeScript type checking |
pnpm lint |
ESLint |
pnpm format |
Prettier |
pnpm docs:dev |
Local docs with hot reload |
- Always run
pnpm buildfirst. Example apps depend on built@sqlrooms/*packages viaworkspace:*links and will not work otherwise. - To run an example:
pnpm dev <name>-example( is the example/ directory name, Vite defaults to port 5173+) pnpm devat the root watches packages for rebuilds — it does NOT start an app. You still needpnpm devinside an example directory.- Lint baseline: warnings only; 0 errors is the expected state.
- AI examples (e.g.
examples/ai) requireOPENAI_API_KEY. Core examples (minimal,query) do not. - When using Zustand/room-store selectors in React, do not create derived arrays/objects/functions inside the selector (for example
state.items.filter(...)). That returns a new reference on every read and can triggeruseSyncExternalStoresnapshot loops (The result of getSnapshot should be cached) and max update depth errors. Select stable raw state first, then derive withReact.useMemoin the component.
- When adding a new grouped UI surface with shared state, prefer a compound component API over repeating the same state prop across multiple siblings.
- Preferred pattern:
- export a top-level compound component that provides context, for example
<Feature>or<Feature.Root> - expose subcomponents like
<Feature.Header />,<Feature.Rows />,<Feature.StatusBar /> - keep the lower-level hook or prop-based primitives available underneath when advanced composition is still valuable
- export a top-level compound component that provides context, for example
- For profiler-style features, prefer the compound form for docs, examples, and new call sites:
- use
<MosaicProfiler ...>or<MosaicProfiler.Root profiler={...}> - then render
<MosaicProfiler.Header />,<MosaicProfiler.Rows />, and related subcomponents inside
- use
- Keep the context layer thin and stable. Avoid turning a compound provider into a second state system; it should usually wrap an existing hook/store API rather than replace it.
- If the shared state is more complex than a few props, prefer managing it in a Zustand store rather than in large prop bags or deeply nested local React state.
- State ownership guidance:
- use the room Zustand store when the state is app-level, cross-panel, or needs to coordinate with other room features
- use an internal feature-local Zustand store when the state is instance-scoped to a component family or compound API
- keep the compound context/provider as a thin access layer over that state, not as the primary place where complex state is modeled
Load these only when you need them:
- Architecture & core concepts
- State patterns (produce, selectors, lifecycle)
- Typescript and React guidelines
- Adding features (packages, visualizations, schema)
- Troubleshooting
- Python workspace
- Cursor Cloud setup
- Contributing guidelines (human contributor process: PRs, code of conduct)