npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

@kepler.gl/sqlrooms

v3.3.0-alpha.13

Published

Kepler.gl maps and configuration for SQLRooms applications

Downloads

184

Readme

@kepler.gl/sqlrooms

Kepler.gl integration for SQLRooms.

Use this package when you want a map-first analytics experience in a SQLRooms app, backed by DuckDB tables and SQL.

What this package provides

  • createKeplerSlice() to add Kepler state/actions to your Room store
  • KeplerMapContainer and KeplerPlotContainer for rendering maps/overlays
  • KeplerSidePanels for layer/filter/interaction UI
  • utilities for map config persistence, dataset synchronization, and migration from legacy Kepler-owned tabs to artifact-backed tabs

Selection model

  • createKeplerSlice() manages Kepler map documents and runtime state keyed by map id, but it does not own host-level map selection.
  • Render maps with explicit ids, for example <KeplerMapContainer mapId={id} />.
  • Use @sqlrooms/artifacts when an app needs multiple user-managed map tabs; artifact state should own the selected map artifact.

Installation

npm install @kepler.gl/sqlrooms @sqlrooms/room-shell @sqlrooms/duckdb @sqlrooms/ui

Quick start

import {useEffect} from 'react';
import {createKeplerSlice, KeplerMapContainer, KeplerSliceState} from '@kepler.gl/sqlrooms';
import {
  createRoomShellSlice,
  createRoomStore,
  RoomShell,
  RoomShellSliceState
} from '@sqlrooms/room-shell';

type RoomState = RoomShellSliceState & KeplerSliceState;

export const {roomStore, useRoomStore} = createRoomStore<RoomState>((set, get, store) => ({
  ...createRoomShellSlice({
    config: {
      dataSources: [
        {
          type: 'url',
          tableName: 'earthquakes',
          url: 'https://huggingface.co/datasets/sqlrooms/earthquakes/resolve/main/earthquakes.parquet'
        }
      ]
    }
  })(set, get, store),
  ...createKeplerSlice()(set, get, store)
}));

function MapPanel() {
  const mapId = useRoomStore(state => state.kepler.config.maps[0]?.id);
  const addTableToMap = useRoomStore(state => state.kepler.addTableToMap);
  const isTableReady = useRoomStore(state => Boolean(state.db.findTable('earthquakes')));

  useEffect(() => {
    if (!isTableReady || !mapId) return;
    void addTableToMap({
      mapId,
      tableName: 'earthquakes',
      options: {
        autoCreateLayers: true,
        centerMap: true
      }
    });
  }, [isTableReady, mapId, addTableToMap]);

  if (!mapId) return null;

  return <KeplerMapContainer mapId={mapId} />;
}

export function App() {
  return (
    <RoomShell roomStore={roomStore} className="h-screen">
      <MapPanel />
    </RoomShell>
  );
}

Adding tables to maps

Use state.kepler.addTableToMap() to load a DuckDB table into a Kepler map. The preferred API is an object parameter:

await state.kepler.addTableToMap({
  mapId,
  tableName: 'earthquakes',
  options: {
    autoCreateLayers: true,
    centerMap: true
  }
});

tableName is the SQL table reference to load. It can also be a saved Kepler dataset id when the configured table-selection policy can resolve that id back to a DuckDB table.

For normal add-table flows, omit datasetId. SQLRooms derives the persisted Kepler dataset id from the table-selection policy:

await state.kepler.addTableToMap({
  mapId,
  tableName: 'main.places'
});

Only pass datasetId when you need to load a table under an existing Kepler dataId, such as when restoring a saved map config:

await state.kepler.addTableToMap({
  mapId,
  tableName: savedDataId,
  options: {
    autoCreateLayers: false,
    centerMap: false
  },
  datasetId: savedDataId
});

The older positional signature is still accepted for compatibility, but new code should use the object form so the table reference, Kepler options, config, and dataset-id override remain clear at the call site.

An optional signal: AbortSignal lets a caller discard a load's results if it is cancelled before they are applied. The promise resolves without adding data; the underlying database query is not cancelled. Overrides of addTableToMap should forward or honor this signal. The positional form accepts it in its final load-options argument.

Common customization

Restoring saved maps

Use kepler.setConfig(savedConfig) to replay configuration received after room initialization, then await kepler.waitForConfigRestore() to wait for the restore operation. Initial configuration and later restores both load datasets referenced by pending layers, filters, and tooltips for every registered map; mounting a map component is not required.

A newer restore cancels results from earlier dataset-sync requests. Explicit addTableToMap calls remain independent of restores unless the caller supplies a cancellation signal; restoring another map does not discard a requested table.

If a referenced table is unavailable or fails to load, the map's saved config is preserved. Deferred Kepler actions cannot replace it with a partially restored config. Refresh the available tables and call await kepler.syncKeplerDatasets() to retry. Other, fully restored maps continue to autosave normally, including intentional deletion of all their layers.

waitForConfigRestore() does not guarantee that unavailable datasets have loaded. Persistence protection is based on each map's pending config, so it remains in effect after that promise resolves. Edits to an incomplete map do not replace its saved config until its pending config is resolved.

Use kepler.isMapConfigPending(mapId) to observe that per-map condition without inspecting Kepler's internal merge fields. It reads current state and returns a boolean, so it can be used directly in a Zustand selector inside a React component:

import {useStoreWithKepler} from '@kepler.gl/sqlrooms';

const configPending = useStoreWithKepler(state => state.kepler.isMapConfigPending(mapId));
// Show a warning that changes to this map are not being saved while pending.

The result stays true if required data remains unavailable after waitForConfigRestore() resolves, and becomes false once the pending config and dataset merges are resolved. Other maps are checked independently. Unknown, unregistered, and deleted maps return false; that does not mean they are ready to render. This status does not include the slice's temporary persistence pause or guarantee completion of all async work, and it does not itself disable editing.

duplicateMap copies a pending map's last preserved saved config, including unresolved layers and settings. It does not wait for missing datasets. If no saved config is available, it returns success: false with code source-map-config-pending without creating a copy. Fully restored maps are duplicated from their current runtime state, including unsaved edits.

Hosts that override dataset synchronization can reuse the same discovery and persistence checks from the public package:

import {
  getReferencedKeplerDatasetIds,
  hasPendingKeplerConfig
} from '@kepler.gl/sqlrooms';

const map = roomStore.getState().kepler.map[mapId];
if (map) {
  const referencedIds = getReferencedKeplerDatasetIds(map.visState);
  const missingIds = [...referencedIds].filter(id => !map.visState.datasets[id]);
  const configPending = hasPendingKeplerConfig(map.visState);
  // Use missingIds in custom loading logic and configPending in host UI.
}

Dataset discovery includes live and pending layers/filters and pending tooltip references. Split-map state references layer ids rather than dataset ids; its pending state is included in hasPendingKeplerConfig. That boolean reports pending config and dataset merges, not the slice's temporary persistence pause or completion of all async work. The helpers do not expose the base sync's restore cancellation signal; custom sync implementations still own cancellation.

getReferencedKeplerDatasetIds returns a new set. With Zustand, select the raw visState and derive that set outside the selector. The boolean helper can be used directly in a selector to observe whether a map has pending config.

Appearance

Pass options to createKeplerSlice():

import {createKeplerTheme, type KeplerThemeOverrides} from '@kepler.gl/sqlrooms';

createKeplerSlice({
  basicKeplerProps: {
    mapboxApiAccessToken: import.meta.env.VITE_MAPBOX_TOKEN
  },
  keplerTheme: createKeplerTheme({
    modalOverLayZ: 40
  } satisfies KeplerThemeOverrides),
  modalPortalTarget: 'body',
  actionLogging: false
});

Notes:

  • basicKeplerProps is for base Kepler registration/component props.
  • keplerTheme (optional) sets the theme passed to Kepler ThemeProvider.
  • modalPortalTarget controls modal portal placement: 'container' (default) or 'body'.

Table selection and dataset ids

tableSelection controls which DuckDB tables Kepler exposes and how those tables are represented in persisted Kepler layer and filter config.

createKeplerSlice({
  tableSelection: {
    defaultDbSchema: {
      database: 'project',
      schema: 'main'
    },
    includeTable: table => table.table.database === 'project'
  }
});

By default, tables in defaultDbSchema use bare dataset ids such as places. Tables outside that database/schema use qualified SQL table references. This keeps common main-schema project tables readable while preserving enough identity for tables from other schemas.

Use includeTable to hide tables from Kepler's Add Layer UI and to skip matching saved dataset ids during dataset synchronization. Host apps commonly use this to hide attached databases that will not be available when a project is reopened.

If the default dataset-id policy is not right for your app, provide both getDatasetIdForTable and findTableForDatasetId so new layers and restored layers agree on the same identity scheme:

createKeplerSlice({
  tableSelection: {
    getDatasetIdForTable: table => `${table.table.schema}:${table.table.table}`,
    findTableForDatasetId: (tables, datasetId) => {
      const [schema, tableName] = datasetId.split(':');
      return tables.find(table => table.table.schema === schema && table.table.table === tableName);
    },
    getTableLabel: table => [table.table.schema, table.table.table].filter(Boolean).join('.')
  }
});

getTableLabel only affects display labels in Kepler table selectors. It does not change persisted dataset ids.

Related packages

  • @sqlrooms/artifacts for artifact-backed map tabs
  • @kepler.gl/sqlrooms/config is a lightweight entry point in this package for persisted config schemas and migrations
  • @sqlrooms/room-shell for Room store composition and UI shell
  • @sqlrooms/duckdb for DuckDB-backed table loading/querying

Examples

Migration and package boundaries

This package incorporates the implementation of @sqlrooms/kepler and @sqlrooms/kepler-config from SQLRooms commit 26d8e78e086cebdfc4eb6b4047c9035c5704a068 under its MIT license. The original license is included in LICENSE.

It also includes the saved-map hydration and per-map pending-status changes from SQLRooms #920 (a986e5e0a2745973254a335b605907d179251cb5) and #922 (104011c4b174b93fc7c0d2c731b28bc9786c428f).

Replace runtime imports from @sqlrooms/kepler with @kepler.gl/sqlrooms. Replace schema-only imports from @sqlrooms/kepler-config with:

import {
  KeplerMapSchema,
  KeplerSliceConfig,
  migrateKeplerTabsToArtifacts
} from '@kepler.gl/sqlrooms/config';

The /config entry point does not import React, Kepler rendering, or the SQLRooms runtime. The same schemas remain re-exported from the main entry point. The serialized map envelope and legacy tab migration retain their original format; moving the package does not require rewriting saved maps.

The adapter depends on Kepler and SQLRooms. Kepler's foundational packages must not depend on this adapter. SQLRooms packages are pinned to the tested release 0.29.0; Kepler packages follow this repository's version. Keep React, React Redux, React Intl, styled-components, and the deck.gl/luma.gl stack shared within the host application. SQLRooms room-store, room-shell, and UI contexts must also resolve to one copy (see the SQLRooms demo esbuild aliases). Do not force all third-party versions of Immer to one version; older Redux Toolkit dependencies require their own supported copy.

Use the SQLRooms connector as the application's database owner. Do not also initialize a separate Kepler DuckDB adapter for the same project. This adapter loads Arrow results into Kepler datasets; it does not promise SQL filter pushdown or automatic refresh of datasets already loaded in a map.

Factory recipes and Kepler application configuration currently have global scope. Configure them before rendering. Host storage, project save/reopen, and locale policy are separate from the config schemas. The SQLRooms demo retains its existing implementations of these features while migrating its shell.

The SQLRooms repository's old packages are intentionally not removed by this Kepler PR. After this package is published, a coordinated SQLRooms change can update consumers and deprecate or re-export the old package names.

Development

From the repository root:

yarn install --immutable
yarn workspaces foreach -At run stab
yarn workspace @kepler.gl/sqlrooms build
yarn workspace @kepler.gl/sqlrooms test
yarn start:sqlrooms

build produces CommonJS and ESM output. Run build:types after building the workspace dependencies and their declarations, as in the repository release workflow. test includes config entry-point checks and adapter/task runtime regressions. The upstream hydration regressions run with the repository Jest suite:

yarn jest --runInBand --runTestsByPath src/sqlrooms/test/KeplerSlice.hydration.spec.ts

The SQLRooms demo uses local Kepler sources and published SQLRooms packages, so no sibling SQLRooms checkout is required. The original demo and website remain unchanged for comparison.

Experimental application shell

@kepler.gl/sqlrooms/shell exports KeplerAppShell and SqlroomsSidebarFactory. Pair the shell with the original injected Kepler application and sidePanelWidth={0}. The shell reserves sidebar space; the recipe portals the original sidebar content into it, preserving provider, localization, Redux, and drag-and-drop contexts. The existing logo/version, data and layer panels, and export/storage menus remain Kepler components. The SQLRooms demo uses SQLRooms LayoutRenderer and a room layout slice for map, SQL, and assistant panels, with a ChordShell-style sidebar toggle.

This migrates application composition first. The SQLRooms demo keeps its existing Redux map store, data loading, saved-map format, URLs, cloud providers, and query integration. It does not convert all maps to KeplerSlice or to DuckDB tables. Applications designed around SQLRooms-owned maps can still compose KeplerSlice and the smaller map/panel components exported from the package root.