Duck-UIDuck-UI

Web Components

Framework-agnostic Custom Elements for SQL-powered dashboards

Web Components

Duck-UI Web Components (@duck_ui/elements) let you build SQL-powered dashboards in any framework -- or no framework at all. They wrap @duck_ui/core as standard Custom Elements.

Installation

With a bundler

bun add @duck_ui/elements
import '@duck_ui/elements'

All elements are registered on import. No further setup needed.

CDN (no bundler)

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

Minimal Example

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

<duck-provider> boots DuckDB-WASM, fetches the data file, loads it into a table, and <duck-table> renders it with pagination and sorting.

Full Dashboard

<duck-provider src="/data/orders.json" table="orders" theme="dark">
  <duck-dashboard columns="3" gap="16">
    <duck-panel span="3">
      <duck-filter-bar auto source="orders"></duck-filter-bar>
    </duck-panel>

    <duck-panel>
      <duck-kpi
        sql="SELECT SUM(total) AS value FROM orders"
        label="Revenue"
        format="currency"
      ></duck-kpi>
    </duck-panel>
    <duck-panel>
      <duck-kpi
        sql="SELECT COUNT(*) AS value FROM orders"
        label="Orders"
        format="number"
      ></duck-kpi>
    </duck-panel>
    <duck-panel>
      <duck-kpi
        sql="SELECT AVG(total) AS value FROM orders"
        label="Avg Order"
        format="currency"
      ></duck-kpi>
    </duck-panel>

    <duck-panel span="2">
      <duck-chart
        sql="SELECT product, SUM(total) AS rev FROM orders GROUP BY 1"
        type="bar"
        height="300"
        title="Revenue by Product"
      ></duck-chart>
    </duck-panel>
    <duck-panel>
      <duck-chart
        sql="SELECT status, COUNT(*) AS n FROM orders GROUP BY 1"
        type="bar"
        height="300"
        title="By Status"
      ></duck-chart>
    </duck-panel>

    <duck-panel span="3">
      <duck-table sql="SELECT * FROM orders" page-size="20" sortable striped></duck-table>
    </duck-panel>

    <duck-panel>
      <duck-export sql="SELECT * FROM orders" format="csv" file-name="orders" label="Export CSV"></duck-export>
    </duck-panel>
  </duck-dashboard>
</duck-provider>

Elements Reference

All attributes use kebab-case. Boolean attributes are toggled by presence (e.g., sortable with no value means true).

<duck-provider>

Root element. Required as an ancestor of all other duck elements.

AttributeDescription
srcURL to a data file (CSV, JSON, Parquet)
tableDuckDB table name for the loaded data
formatFile format (csv, json, parquet). Auto-detected from URL extension if omitted.
themelight or dark

<duck-chart>

AttributeDescription
sqlSQL query (required)
typeline, bar, area, or scatter (default: line)
heightHeight in pixels (default: 300)
widthWidth in pixels (default: fills container)
titleChart title

<duck-table>

AttributeDescription
sqlSQL query (required)
page-sizeRows per page (default: 25)
sortableEnable column sorting
stripedAlternate row colors
max-heightMax scroll container height (e.g., 400px)

<duck-kpi>

AttributeDescription
sqlQuery returning a single value (required)
labelKPI label (required)
formatcurrency, percent, number, or compact
currencyCurrency code (default: USD)
compare-sqlSQL for comparison value (shows % change)
sparkline-sqlSQL for inline trend line

<duck-dashboard> + <duck-panel>

Grid layout. <duck-dashboard> is the container, <duck-panel> wraps each item.

ElementAttributeDescription
duck-dashboardcolumnsGrid columns (default: 2)
duck-dashboardgapGap in px (default: 16)
duck-dashboardpaddingPadding in px (default: 24)
duck-panelspanColumn span (default: 1)
duck-panelrow-spanRow span (default: 1)

<duck-filter-bar>

AttributeDescription
autoAuto-detect filters from table schema
sourceTable name for distinct values

Individual Filters

ElementAttributes
<duck-select-filter>column (required), source, label
<duck-range-filter>column (required), min (required), max (required), step, label
<duck-date-filter>column (required), label
<duck-filter-bar>
  <duck-select-filter column="status" source="orders" label="Status"></duck-select-filter>
  <duck-range-filter column="total" min="0" max="1000" step="10" label="Total"></duck-range-filter>
  <duck-date-filter column="created_at" label="Date"></duck-date-filter>
</duck-filter-bar>

<duck-export>

AttributeDescription
sqlSQL query to export (required)
formatcsv or json (default: csv)
file-nameFile name without extension (default: export)
labelButton label (default: Export)

Events

Events are standard CustomEvent instances dispatched on the <duck-provider> element.

EventdetailWhen
duck-ready{}Engine initialized and data loaded
duck-error{ error: Error }Initialization or query error
duck-filter-change{ filters: Record<string, FilterValue> }A filter value changed
document.querySelector('duck-provider').addEventListener('duck-ready', () => {
  console.log('Dashboard is ready')
})

document.querySelector('duck-provider').addEventListener('duck-filter-change', (e) => {
  console.log('Filters:', e.detail.filters)
})

document.querySelector('duck-provider').addEventListener('duck-error', (e) => {
  console.error('Error:', e.detail.error)
})

Theming with CSS Custom Properties

Style all Duck-UI elements using CSS custom properties. Set them on <duck-provider> or any ancestor element.

duck-provider {
  --duck-bg: #ffffff;
  --duck-text: #374151;
  --duck-primary: #2563eb;
  --duck-surface: #f9fafb;
  --duck-border: #e5e7eb;
  --duck-grid: #e5e7eb;
  --duck-hover: #f3f4f6;
  --duck-muted: #9ca3af;
  --duck-error: #ef4444;
  --duck-success: #22c55e;
}

Dark theme example:

duck-provider[theme="dark"] {
  --duck-bg: #111827;
  --duck-text: #e5e7eb;
  --duck-primary: #60a5fa;
  --duck-surface: #1f2937;
  --duck-border: #374151;
  --duck-grid: #374151;
  --duck-hover: #1f2937;
  --duck-muted: #6b7280;
}

Or apply a fully custom theme:

.corporate-dashboard duck-provider {
  --duck-bg: #fafafa;
  --duck-text: #1a1a1a;
  --duck-primary: #0066cc;
  --duck-surface: #ffffff;
  --duck-border: #e0e0e0;
}

Framework Integration

Web Components work natively in HTML. Most frameworks also handle them well.

Vue

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

<script setup>
import '@duck_ui/elements'
</script>

Svelte

<script>
  import '@duck_ui/elements'
</script>

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

Angular

Add CUSTOM_ELEMENTS_SCHEMA to your module or standalone component:

import { Component, CUSTOM_ELEMENTS_SCHEMA } from '@angular/core'
import '@duck_ui/elements'

@Component({
  selector: 'app-dashboard',
  schemas: [CUSTOM_ELEMENTS_SCHEMA],
  template: `
    <duck-provider src="/data/orders.json" table="orders">
      <duck-table sql="SELECT * FROM orders" sortable></duck-table>
    </duck-provider>
  `,
})
export class DashboardComponent {}

Astro

---
---
<script>
  import '@duck_ui/elements'
</script>

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

Parquet Files

Point <duck-provider> at a Parquet file for efficient range-request loading. DuckDB only downloads the row groups it needs.

<duck-provider src="https://my-cdn.com/data/sales.parquet" table="sales" format="parquet">
  <duck-chart sql="SELECT month, SUM(revenue) AS rev FROM sales GROUP BY 1 ORDER BY 1" type="line" height="300"></duck-chart>
</duck-provider>

Next Steps

  • Core API -- use the DuckUI class directly for maximum control
  • API Reference -- full attribute and event reference
  • Theming -- detailed theme customization
  • Filters -- deep dive into the filter system

On this page