@duckviz/db
DuckDB-WASM engine with React context, file ingestion, persistence, and SQL queries.
@duckviz/db provides a React context that wraps DuckDB-WASM. It handles engine initialization, file ingestion, SQL queries, IndexedDB persistence, and table management — all running in a Web Worker so the main thread stays responsive.
Installation
npm install @duckviz/db @duckdb/duckdb-wasm apache-arrowQuick start
import { DuckvizDBProvider, useDuckDB } from "@duckviz/db";
// 1. Wrap your app
function App() {
return (
<DuckvizDBProvider persistence batchSize={5000}>
<MyComponent />
</DuckvizDBProvider>
);
}
// 2. Use the hook
function MyComponent() {
const db = useDuckDB();
async function loadData() {
await db.ingest({
rows: [{ name: "Alice", age: 30 }, { name: "Bob", age: 25 }],
tableName: "t_users",
});
const result = await db.runQuery("SELECT * FROM t_users");
console.log(result);
}
if (db.loading) return <p>Booting DuckDB...</p>;
return <button onClick={loadData}>Load data</button>;
}<DuckvizDBProvider>
The provider initializes DuckDB-WASM and makes it available to child components via context.
Prop
Type
defaultRowCap was added in @duckviz/db@0.6.0; the non-SELECT skip fix landed in 0.6.1. See Performance for sizing guidance.
useDuckDB()
Returns the DuckDBContextValue object. Throws if used outside a <DuckvizDBProvider>.
State properties
Prop
Type
Ingestion methods
Prop
Type
Query methods
Prop
Type
Persistence methods
Prop
Type
useDuckDBOrNull()
Same as useDuckDB(), but returns null instead of throwing when used outside a provider. Useful for components that can optionally use DuckDB.
useAutoIngest()
Hook that auto-ingests row data into DuckDB on mount. Used internally by <Dashboard>, <Explorer>, <ReportBuilder>, and <DeckBuilder> — call it directly if you're composing your own UI.
import { useAutoIngest, type DuckvizDataset } from "@duckviz/db";
const datasets: DuckvizDataset[] = [
{ name: "Orders", data: orderRows }, // table: t_orders
{ name: "Users", data: userRows, tableName: "t_users" },
];
function MyComponent() {
const { ready, ingesting, error } = useAutoIngest({
datasets,
dropOnUnmount: true, // default
});
if (ingesting || !ready) return <p>Loading…</p>;
if (error) return <p>Failed: {error}</p>;
return <YourChart />;
}Re-ingestion is keyed on a fingerprint of (tableName, rowCount) per dataset — stable references are not required. Handles React Strict Mode double-mount via a microtask-deferred drop.
DuckvizDataset
Shared dataset shape used by auto-ingest across all DuckViz component packages.
interface DuckvizDataset {
name: string; // Display name, also used to derive tableName when omitted
data: Record<string, unknown>[]; // Row data
tableName?: string; // Explicit DuckDB table name (overrides pathToTableName(name))
}pathToTableName()
Utility function that converts a file path to a valid DuckDB table name:
import { pathToTableName } from "@duckviz/db";
pathToTableName("sales_2024.csv"); // "t_sales_2024"
pathToTableName("logs/access.log"); // "t_access"
pathToTableName("data.json"); // "t_data"Strips file extensions, adds a t_ prefix, and sanitizes special characters.
Exported types
// Column metadata
interface SchemaColumn {
column_name: string;
column_type: string; // DuckDB type: VARCHAR, INTEGER, DOUBLE, etc.
}
// Table metadata
interface TableInfo {
tableName: string;
schema: SchemaColumn[];
}Example: programmatic data seeding
import { useDuckDB } from "@duckviz/db";
import { useEffect, useRef } from "react";
function DataSeeder() {
const db = useDuckDB();
const seeded = useRef(false);
useEffect(() => {
if (db.loading || db.restoring || seeded.current) return;
seeded.current = true;
(async () => {
// Check if table already exists (from persisted session)
const exists = await db.tableExists("t_users");
if (exists) return;
// Fetch data from your API
const res = await fetch("/api/users");
const { users } = await res.json();
// Ingest into DuckDB
await db.ingest({ rows: users, tableName: "t_users" });
// Now you can query it
const topUsers = await db.runQuery(`
SELECT name, revenue
FROM t_users
ORDER BY revenue DESC
LIMIT 10
`);
console.log(topUsers);
})();
}, [db]);
return null;
}