Skip to content

Overview

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 example apps

The framework is designed for developers building innovative data tools and it tackles several common analytics challenges:

  • Interactive data analysis tools
  • Custom BI solutions
  • Data visualization applications
  • Internal analytics dashboards

Explore the Examples gallery or the Case Studies for real‑world setups.

Why SQLRooms?

  • Performance & Scale: Each user gets an in‑browser DuckDB instance with columnar speed and zero backend load.
  • Modular Architecture: Mix-and-match packages and combine state slices to include only the functionality you need.
  • AI‑Powered Analytics: Local SQL execution lets AI agents generate and run queries instantly.
  • Privacy & Security: All data can stay client‑side for simplified compliance.

Modular Architecture

SQLRooms is designed with a modular architecture that allows developers to pick and choose exactly the functionality they need for their data analytics applications. This approach enables you to build custom solutions tailored to your specific requirements.

SQLRooms Architecture

Key Concepts

What's a Room?

A Room is a self-contained workspace where users can explore datasets, run queries, and view results. The term comes from collaborative tools—where users work in shared spaces—and SQLRooms is built with future real-time collaboration in mind.

A Room consists of:

  • <RoomShell>: a React component that renders the Room UI
  • roomStore: a Zustand-based state store for the Room

Room Store

The roomStore is a composable Zustand store created by calling createRoomStore(). The store holds:

  • config: the persistable part of the state that captures a Room's saveable settings and can be serialized to JSON for storage or sharing including:
    • the view configuration and the layout state
    • the user preferences
  • room: non-persistable state that holds runtime information like:
    • loaded DuckDB tables
    • transient UI state (like "query running")
tsx
const {roomStore, useRoomStore} = createRoomStore<RoomConfig, RoomState>(
  (set, get, store) => ({
    ...createRoomShellSlice<RoomConfig>({
      config: {
        dataSources: [
          {
            type: 'url',
            url: 'https://.../earthquakes.parquet',
            tableName: 'earthquakes',
          },
        ],
      },
      room: {
        // Runtime state initialization…
      },
    })(set, get, store),
  }),
);

Check the minimal example for the complete implementation.


RoomShell

<RoomShell> is a React component that wraps your Room UI

  • It injects the roomStore into React context, accessible via the useRoomStore() hook
  • It sets up essential UI infrastructure including error boundaries, toast notifications, and tooltips, making it easy to use components from @sqlrooms/ui out of the box
  • It provides slots for the optional LayoutComposer (see Layout section below), Sidebar, and LoadingProgress components
tsx
const App = () => (
  <RoomShell roomStore={roomStore}>
    <MyComponent />
  </RoomShell>
);

SQL and DuckDB Access

SQLRooms includes a built-in DuckDB integration via the DuckDbSlice. The DuckDbSlice provides helper functions for managing and querying tables:

  • findTableByName() - Look up a table by name in the current schema
  • addTable() - Add a new table from Arrow data or records
  • dropTable() - Remove a table from the database
  • refreshTableSchemas() - Update the cached table schemas
  • tables - The cached list of tables from the last refreshTableSchemas() call
  • getConnector() - Access the underlying DuckDB connector

You can query your datasets using the useSql(query) hook and work directly with Arrow tables in React.

tsx
function MyComponent() {
  const isTableReady = useRoomStore((state) =>
    Boolean(state.db.findTableByName('earthquakes')),
  );
  const queryResult = useSql<{maxMagnitude: number}>({
    query: `SELECT max(Magnitude) AS maxMagnitude FROM earthquakes`,
    enabled: isTableReady,
  });
  const row = queryResult.data?.toArray()[0];
  return row ? `Max earthquake magnitude: ${row.maxMagnitude}` : <Spinner />;
}

For more details on DuckDB integration and available methods, see the DuckDB API Reference.


Composing Store from Slices

The store can be enhanced with slices—modular pieces of state and logic that can be added to your Room. You can use slices from the @sqlrooms/* packages or create your own custom slices. Each slice is a function that returns a partial state object along with methods to modify that state.

Here's an example showing how to combine the default room shell with SQL editor functionality:

tsx
const {roomStore, useRoomStore} = createRoomStore<RoomConfig, RoomState>({
  // Default slice
  ...createRoomShellSlice<RoomConfig>({
    config: {
      // Add SQL editor slice persistable config
      ...createDefaultSqlEditorConfig(),
    },
    room: {},
  })(set, get, store),

  // Mix in sql editor slice
  ...createSqlEditorSlice()(set, get, store),
});

You can access slices' namespaced config, state and functions in the store using selectors, for example:

tsx
const queries = useRoomStore((state) => state.config.sqlEditor.queries);
const runQuery = useRoomStore((state) => state.sqlEditor.parseAndRunQuery);

Learn more about store and slices in State Management.


Layout (Optional)

The LayoutComposer provides a flexible panel layout for your Room's UI.

  • Panels are React components that can be plugged into the layout. They include metadata (id, title, icon) and a component to render.
  • Panels can be moved, resized, or hidden
  • Developers can add panels by registering them in the roomStore.
  • Layout state is persisted in the roomStore

Configure the room layout and panels during store initialization:

tsx
const {roomStore, useRoomStore} = createRoomStore<RoomConfig, RoomState>(
  (set, get, store) => ({
    ...createRoomShellSlice<RoomConfig>({
      config: {
        layout: {
          type: LayoutTypes.enum.mosaic,
          nodes: {
            // Data panel on left (30%) and main view on right
            direction: 'row',
            first: 'data-panel',
            second: MAIN_VIEW,
            splitPercentage: 30,
          },
        },
      },
      room: {
        // Define the available panels in the room layout
        panels: {
          'data-panel': {
            title: 'Data Sources',
            icon: DatabaseIcon,
            component: DataSourcesPanel,
            placement: 'sidebar',
          },
          main: {
            title: 'Main view',
            icon: () => null,
            component: MainView,
            placement: 'main',
          },
        },
      },
    })(set, get, store),
  }),
);

Layout composer renders the mosaic layout with panels:

tsx
function App() {
  return (
    <RoomShell className="h-screen" roomStore={roomStore}>
      <RoomShell.Sidebar />
      <RoomShell.LayoutComposer />
    </RoomShell>
  );
}

For more details on layout configuration and customization, see the Layout API Reference.

Next Steps