Duck-UIDuck-UI

Getting Started with Duck-UI Embed

Install Duck-UI packages and build SQL-powered analytics dashboards in React, Web Components, or plain HTML

Getting Started

Get a SQL-powered analytics dashboard running in 5 minutes. Duck-UI is framework-agnostic -- choose the integration that fits your stack.

PackageUse caseSize
@duck_ui/corePure JS engine, chart factories, no frameworkSmallest
@duck_ui/embedReact bindings (imports from core)Core + React wrappers
@duck_ui/elementsWeb Components / Custom Elements (imports from core)Core + CE wrappers
@duck_ui/cdnPre-bundled IIFE script, no build stepAll-in-one

Prerequisites

  • Node.js 18+ (for npm/bun installs)
  • React 18+ (only for @duck_ui/embed)
  • A package manager (bun recommended)

Installation

React

bun add @duck_ui/embed @duckdb/duckdb-wasm

Or with npm:

npm install @duck_ui/embed @duckdb/duckdb-wasm

Web Components

bun add @duck_ui/elements

Or with npm:

npm install @duck_ui/elements

@duck_ui/elements bundles @duck_ui/core and @duckdb/duckdb-wasm as dependencies -- no extra peer installs needed.

CDN (no build step)

<script src="https://unpkg.com/@duck_ui/cdn/dist/duck-ui.min.js"></script>

This registers all Web Components globally and exposes window.DuckUI for imperative use. No bundler required.

Core only (advanced)

bun add @duck_ui/core @duckdb/duckdb-wasm

Use this when you want the DuckDB engine and chart factories without any UI components. See the Core API guide.


Quick Start: React

Wrap your app with DuckUIProvider, pass data, and drop in components:

import { DuckUIProvider, DataTable } from '@duck_ui/embed'

const orders = [
  { id: 1, product: 'Widget', status: 'shipped', total: 99.50 },
  { id: 2, product: 'Gadget', status: 'pending', total: 149.00 },
]

function App() {
  return (
    <DuckUIProvider data={{ orders }}>
      <DataTable sql="SELECT * FROM orders" />
    </DuckUIProvider>
  )
}

That's it. DuckDB-WASM boots in a Web Worker, loads the array into a orders table, and the DataTable renders it with pagination and sorting.

Quick Start: Web Components

<script src="https://unpkg.com/@duck_ui/cdn/dist/duck-ui.min.js"></script>

<duck-provider src="/data/orders.json" table="orders">
  <duck-table sql="SELECT * FROM orders" page-size="25" sortable></duck-table>
</duck-provider>

Or with a bundler:

import '@duck_ui/elements'
<duck-provider src="/data/orders.json" table="orders">
  <duck-table sql="SELECT * FROM orders" page-size="25" sortable></duck-table>
</duck-provider>

Web Components work in any framework (Vue, Svelte, Angular, Astro) or plain HTML. See the Web Components guide for full details.


Add a Chart

React

import { DuckUIProvider, DataTable, Chart } from '@duck_ui/embed'

function App() {
  return (
    <DuckUIProvider data={{ orders }}>
      <Chart
        sql="SELECT product, SUM(total) AS revenue FROM orders GROUP BY 1"
        type="bar"
        height={300}
      />
      <DataTable sql="SELECT * FROM orders" pageSize={25} />
    </DuckUIProvider>
  )
}

Web Components

<duck-provider src="/data/orders.json" table="orders">
  <duck-chart
    sql="SELECT product, SUM(total) AS revenue FROM orders GROUP BY 1"
    type="bar"
    height="300"
  ></duck-chart>
  <duck-table sql="SELECT * FROM orders" page-size="25"></duck-table>
</duck-provider>

The first column of the query result becomes the x-axis, remaining columns become series.

Add a KPI

React

import { KPICard } from '@duck_ui/embed'

<KPICard
  sql="SELECT SUM(total) AS value FROM orders"
  label="Total Revenue"
  format="currency"
  compareSql="SELECT SUM(total) AS value FROM orders WHERE year = 2024"
  sparklineSql="SELECT month, SUM(total) AS rev FROM orders GROUP BY 1 ORDER BY 1"
/>

Web Components

<duck-kpi
  sql="SELECT SUM(total) AS value FROM orders"
  label="Total Revenue"
  format="currency"
  compare-sql="SELECT SUM(total) AS value FROM orders WHERE year = 2024"
  sparkline-sql="SELECT month, SUM(total) AS rev FROM orders GROUP BY 1 ORDER BY 1"
></duck-kpi>
  • format accepts 'currency', 'percent', 'number', 'compact', or a custom function (React only)
  • compareSql / compare-sql shows a percentage change vs. the comparison value
  • sparklineSql / sparkline-sql renders an inline trend line

Add Filters

Filters are global -- they automatically inject WHERE clauses into all queries:

React

import {
  DuckUIProvider, DataTable, Chart, KPICard, FilterBar,
} from '@duck_ui/embed'

function Dashboard() {
  return (
    <DuckUIProvider data={{ orders }}>
      <FilterBar auto="orders" />
      <KPICard sql="SELECT SUM(total) AS value FROM orders" label="Revenue" format="currency" />
      <Chart sql="SELECT product, SUM(total) AS rev FROM orders GROUP BY 1" type="bar" height={300} />
      <DataTable sql="SELECT * FROM orders" pageSize={25} />
    </DuckUIProvider>
  )
}

Web Components

<duck-provider src="/data/orders.json" table="orders">
  <duck-filter-bar auto source="orders"></duck-filter-bar>
  <duck-kpi sql="SELECT SUM(total) AS value FROM orders" label="Revenue" format="currency"></duck-kpi>
  <duck-chart sql="SELECT product, SUM(total) AS rev FROM orders GROUP BY 1" type="bar" height="300"></duck-chart>
  <duck-table sql="SELECT * FROM orders" page-size="25"></duck-table>
</duck-provider>

When a user selects a filter value, every component automatically re-queries with the filter applied.

Data Sources

// Array of objects (loaded directly into DuckDB)
<DuckUIProvider data={{ orders: [...] }}>

// Remote file (CSV, JSON, or Parquet)
<DuckUIProvider data={{ sales: { url: '/data/sales.parquet', format: 'parquet' } }}>

// Async fetch callback (call your own API)
<DuckUIProvider data={{ users: { fetch: () => fetch('/api/users').then(r => r.json()) } }}>

// Browser File object (drag & drop, file picker)
<DuckUIProvider data={{ upload: fileFromInput }}>

For Web Components, use src and format attributes on <duck-provider>:

<duck-provider src="/data/sales.parquet" table="sales" format="parquet">
  ...
</duck-provider>

See the Data Sources guide for details.

Bundler Configuration

DuckDB-WASM loads its engine from a CDN (jsDelivr) by default. No special bundler config is needed for most setups.

Vite

Works out of the box. No additional configuration required.

Next.js

Add DuckDB-WASM to the server component exclusion list if using App Router:

// next.config.js
module.exports = {
  webpack: (config) => {
    config.resolve.fallback = { ...config.resolve.fallback, fs: false, path: false }
    return config
  },
}

Ensure your Duck-UI components are client-only:

'use client'
import { DuckUIProvider, DataTable } from '@duck_ui/embed'

Webpack

If you need to serve WASM files locally instead of from CDN, configure the asset loader:

// webpack.config.js
module.exports = {
  module: {
    rules: [
      { test: /\.wasm$/, type: 'asset/resource' },
    ],
  },
}

Next Steps

On this page