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.
| Package | Use case | Size |
|---|---|---|
@duck_ui/core | Pure JS engine, chart factories, no framework | Smallest |
@duck_ui/embed | React bindings (imports from core) | Core + React wrappers |
@duck_ui/elements | Web Components / Custom Elements (imports from core) | Core + CE wrappers |
@duck_ui/cdn | Pre-bundled IIFE script, no build step | All-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-wasmOr with npm:
npm install @duck_ui/embed @duckdb/duckdb-wasmWeb Components
bun add @duck_ui/elementsOr 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-wasmUse 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>formataccepts'currency','percent','number','compact', or a custom function (React only)compareSql/compare-sqlshows a percentage change vs. the comparison valuesparklineSql/sparkline-sqlrenders 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
- Core API -- pure JS engine, no framework
- Web Components -- framework-agnostic Custom Elements
- Data Sources -- all data input types
- Gateway Pattern -- connect to Postgres, MySQL, ClickHouse via your backend
- Filters -- deep dive into the filter system
- Charts -- all chart types and customization
- API Reference -- full API reference