Building blocks for React data analytics applications powered by DuckDB
SQLRooms provides a comprehensive foundation and rich set of building blocks for creating modern, interactive data-analytics applications that can run entirely in the browser. At its core is the concept of a Room — a self‑contained workspace where data lives, analysis happens, and (soon) collaborators meet. It combines essential components like a SQL query engine (DuckDB), data visualization tools, state management, and UI components into a cohesive toolkit, making it significantly easier to create powerful analytics tools with or without a backend.
SQLRooms is designed to empower developers and users with a modern, modular analytics toolkit that runs entirely in the browser. Here's what sets it apart:
- Performance & Scale: Every user gets a dedicated in-browser DuckDB instance, delivering columnar analytics speed with zero backend load.
- Modular Architecture: Mix and match packages, and combine state slices to include only the features you need—no bloat, just what your app requires.
- AI‑Powered Analytics: Built-in support for agents that can write and execute SQL queries, and generate insights directly in your browser—no server roundtrips required.
- Developer Experience: A composable, React-based framework with ready-to-use components, state management, and visualization tools, making it easy to build custom analytics solutions.
SQLRooms is designed for single-node analytics: all computation happens on your device, whether in the browser or a desktop app (e.g. via Electron), with no backend required. Data can remain local if you choose, or be loaded from external sources like S3—always giving you full control over how and where your data is processed.
- Privacy: All data remains on your device for simplified compliance and peace of mind—nothing leaves your browser unless you choose.
- Own Your Data: You control your files and data, with no vendor lock-in or forced cloud storage. Your work is portable and future-proof.
- Offline Use: SQLRooms supports offline work—query, analyze, and visualize your data even without an internet connection.
- Fast Local Querying: Queries run instantly in your browser, with no network roundtrip or server lag—results are available as soon as you ask.
- Private AI Insights: AI agents generate insights and run queries locally, so your data is never shared with external model providers. You get the power of AI-driven analytics without sacrificing privacy.
This approach draws on Local-First principles, which emphasize user ownership and seamless collaboration. In Local-First apps, users retain full control of their data — it lives on their device, remains accessible offline, and isn’t locked behind a remote server. By contrast, traditional cloud apps centralize both computation and storage, often reducing user agency. If the service goes down or is discontinued, the app may stop working entirely, and user data can become inaccessible.
While SQLRooms does not yet implement sync or collaboration, it is already capable of delivering some of the key benefits of local-first software — your data and computation can stay private and accessible on your device.
-
Review the Key Concepts to understand the core ideas and architecture.
-
Explore the Modular Architecture to see how you can compose and extend your app.
-
Check the Example Applications to see what can be built with the framework.
SQLRooms is a powerful framework and a set of building blocks for creating DuckDB-backed analytics applications in React. This guide will help you integrate SQLRooms into your application. For a detailed overview of the framework's architecture and core ideas, check out the Key Concepts and Modular Architecture pages.
The Minimal Example is the quickest way to see SQLRooms in action with the smallest possible setup. It demonstrates loading a CSV data source and running SQL queries with useSql() in a barebones Vite + React app.
To create a new project from the minimal example, run:
npx degit sqlrooms/examples/minimal my-minimal-app/
cd my-minimal-app
npm install
npm run devThe Get Started Example is a more feature-rich starter template that demonstrates a typical SQLRooms application structure, including panels, layout, and configuration.
To create a new project from the get-started example, run:
npx degit sqlrooms/examples/get-started myapp/
cd myapp
npm install
npm run devThis Vite application demonstrates loading a CSV data source and running SQL queries with useSql(), along with a more complete app shell and layout.
Your application should have the following dependencies:
- React 18+ (React 19 is supported)
- Tailwind CSS
- Node.js >= 22
SQLRooms uses Zustand for state management and Zod for schema validation internally, but you don't need to install them separately
Install the required SQLRooms packages:
::: code-group
npm install @sqlrooms/room-shell @sqlrooms/duckdb @sqlrooms/uipnpm add @sqlrooms/room-shell @sqlrooms/duckdb @sqlrooms/uiyarn add @sqlrooms/room-shell @sqlrooms/duckdb @sqlrooms/ui:::
You can follow this guide to install and configure Tailwind 3 (Tailwind 4 support is still experimental).
::: code-group
npm install -D tailwindcss@3
npx tailwindcss initpnpm add -D tailwindcss@3
npx tailwindcss inityarn add -D tailwindcss@3
npx tailwindcss init:::
SQLRooms provides a Tailwind preset that includes all the necessary styles. Update your tailwind.config.js or tailwind.config.ts:
import {sqlroomsTailwindPreset} from '@sqlrooms/ui';
import type {Config} from 'tailwindcss';
const config = {
presets: [sqlroomsTailwindPreset()],
content: [
// Your content paths...
'./src/**/*.{ts,tsx}',
// Add SQLRooms packages to content paths
'./node_modules/@sqlrooms/**/dist/**/*.js',
],
} satisfies Config;
export default config;Make sure to import the preset Tailwind styles in your main CSS file:
@import '@sqlrooms/ui/tailwind-preset.css';- Define your application state type:
import {
createRoomShellSlice,
createRoomStore,
RoomShellSliceState,
} from '@sqlrooms/room-shell';
/**
* The whole app state.
*/
export type RoomState = RoomShellSliceState & {
// Add your custom app state types here
// If using additional slices:
// & SqlEditorSliceState
};- Create your room store:
import {LayoutTypes} from '@sqlrooms/room-shell';
import {DatabaseIcon} from 'lucide-react';
import {MainView} from './components/MainView';
import {DataSourcesPanel} from './components/DataSourcesPanel';
/**
* Create the room store. You can combine your custom state and logic
* with the slices from the SQLRooms modules.
*/
export const {roomStore, useRoomStore} = createRoomStore<RoomState>(
(set, get, store) => ({
...createRoomShellSlice({
config: {
title: 'My SQLRooms App',
dataSources: [
{
tableName: 'earthquakes',
type: 'url',
url: 'https://pub-334685c2155547fab4287d84cae47083.r2.dev/earthquakes.parquet',
},
],
},
layout: {
config: {
type: LayoutTypes.enum.mosaic,
nodes: {
direction: 'row',
first: 'data-sources',
second: 'main',
splitPercentage: 30,
},
},
panels: {
'data-sources': {
title: 'Data Sources',
icon: DatabaseIcon,
component: DataSourcesPanel,
placement: 'sidebar',
},
main: {
title: 'Main view',
icon: () => null,
component: MainView,
placement: 'main',
},
},
},
})(set, get, store),
// Add additional slices if needed
// ...createSqlEditorSlice()(set, get, store),
}),
);- Optionally add persistence using the
persistSliceConfigshelper:
import {
BaseRoomConfig,
LayoutConfig,
persistSliceConfigs,
} from '@sqlrooms/room-shell';
export const {roomStore, useRoomStore} = createRoomStore<RoomState>(
persistSliceConfigs(
{
name: 'app-state-storage',
sliceConfigSchemas: {
room: BaseRoomConfig,
layout: LayoutConfig,
// Add other slice configs as needed
// sqlEditor: SqlEditorSliceConfig,
},
},
(set, get, store) => ({
// Store configuration as shown above
...createRoomShellSlice({
config: {
title: 'My SQLRooms App',
dataSources: [],
},
layout: {
config: {
type: LayoutTypes.enum.mosaic,
nodes: {
// layout configuration
},
},
panels: {
// Panel definitions
},
},
})(set, get, store),
}),
),
);Wrap your application with a RoomShell which provides the room store context:
import {RoomShell} from '@sqlrooms/room-shell';
import {ThemeProvider} from '@sqlrooms/ui';
import {roomStore} from './store';
export const Room = () => (
<ThemeProvider defaultTheme="light" storageKey="sqlrooms-ui-theme">
<RoomShell className="h-screen" roomStore={roomStore}>
<RoomShell.Sidebar />
<RoomShell.LayoutComposer />
<RoomShell.LoadingProgress />
</RoomShell>
</ThemeProvider>
);Access the store in your components:
import {useRoomStore} from './store';
function YourComponent() {
// Access config from room
const roomConfig = useRoomStore((state) => state.room.config);
// Access database state
const tables = useRoomStore((state) => state.db.tables);
// Check if a table is ready
const tableReady = useRoomStore((state) =>
state.db.findTableByName('earthquakes'),
);
return (
// Your component JSX
);
}Use the useSql hook from @sqlrooms/duckdb to run SQL queries:
import {useSql} from '@sqlrooms/duckdb';
import {useRoomStore} from './store';
function MainView() {
const tableReady = useRoomStore((state) =>
state.db.findTableByName('earthquakes'),
);
const {data, isLoading, error} = useSql<{
count: number;
maxMag: number;
}>({
query: `
SELECT
COUNT(*)::int AS count,
max(Magnitude) AS maxMag
FROM earthquakes
`,
enabled: Boolean(tableReady),
});
if (isLoading) return <div>Loading...</div>;
if (error) return <div>Error: {error.message}</div>;
const row = data?.toArray()[0];
return (
<div>
<div>Total records: {row?.count}</div>
<div>Max magnitude: {row?.maxMag}</div>
</div>
);
}The useSql hook automatically re-runs queries when the database state changes and provides loading/error states out of the box.
- Start or join a discussion on GitHub Discussions
- File an issue on GitHub
pnpm install
pnpm build
pnpm dev
The documentation is built using VitePress and TypeDoc. To develop the documentation locally:
-
Install dependencies:
pnpm install pnpm build pnpm docs:dev
This will start a local server with hot-reloading at http://localhost:5173
-
Build the documentation:
pnpm docs:build
-
Preview the built documentation:
pnpm docs:preview
This will serve the built documentation at http://localhost:4173
The documentation source files are located in the docs/ directory. API documentation is automatically generated from TypeScript source code using TypeDoc.
This project uses Git Large File Storage (LFS) for managing media files in the documentation. To work with these files:
-
Install Git LFS on your system:
# macOS (using Homebrew) brew install git-lfs # Ubuntu/Debian sudo apt install git-lfs
-
Initialize Git LFS in your local repository:
git lfs install
-
Pull existing LFS files:
git lfs pull
-
When adding new media files to the
docs/media/directory, they will be automatically tracked by Git LFS as specified in the.gitattributesfile.