@duckviz/widgets
Chart component registry with 80+ D3 chart types, alias validator, and an error boundary.
@duckviz/widgets contains every D3 chart used by DuckViz (80+ types across 15 families), plus the alias validator, row-cap helpers, and <ChartErrorBoundary> used by @duckviz/dashboard and @duckviz/explorer. A standalone <WidgetRenderer> runs a SQL query and renders the result.
Installation
npm install @duckviz/widgets @duckviz/db
npm install react react-dom d3Quick start
import { WidgetRenderer } from "@duckviz/widgets";
import { DuckvizDBProvider } from "@duckviz/db";
function App() {
return (
<DuckvizDBProvider persistence>
<WidgetRenderer
widget={{
type: "bar",
title: "Top Categories",
description: "Revenue by category",
duckdbQuery: "SELECT category, SUM(amount) AS value FROM t_sales GROUP BY category ORDER BY value DESC LIMIT 10",
}}
height={300}
/>
</DuckvizDBProvider>
);
}<WidgetRenderer> props
Prop
Type
The widget object shape:
interface RecommendedWidget {
type: string; // Chart type (e.g. "bar", "scatter", "heatmap")
title: string;
description: string;
duckdbQuery: string; // DuckDB SQL query with standardized column aliases
config?: Record<string, unknown>;
}Chart registry API
Look up chart components programmatically:
import { getChartComponent, hasChartComponent } from "@duckviz/widgets";
// Check if a chart type exists
hasChartComponent("bar"); // true
hasChartComponent("unknown"); // false
// Get the React component for a chart type
const BarChart = getChartComponent("bar");
// Returns null if the type doesn't existColumn alias contract
Each chart type expects specific column names in the SQL output. This standardization lets pre-built chart components render any dataset without custom mapping.
Common alias patterns:
| Chart type | Expected columns | Example SQL |
|---|---|---|
bar | category, value | SELECT dept AS category, COUNT(*) AS value FROM ... |
line | category, value | SELECT month AS category, total AS value FROM ... |
scatter | x, y | SELECT age AS x, salary AS y FROM ... |
pie / donut | category, value | SELECT status AS category, COUNT(*) AS value FROM ... |
heatmap | x, y, value | SELECT hour AS x, day AS y, COUNT(*) AS value FROM ... |
treemap | category, value, parent | SELECT name AS category, size AS value, parent FROM ... |
Use CHART_REQUIRED_ALIASES and validateChartAliases to check at runtime:
import { CHART_REQUIRED_ALIASES, validateChartAliases } from "@duckviz/widgets";
// Get required aliases for a chart type
const aliases = CHART_REQUIRED_ALIASES["bar"]; // ["category", "value"]
// Validate that query results have the right columns
const data = await db.runQuery(widget.duckdbQuery);
const valid = validateChartAliases(widget.type, data);Row caps + error boundary
Every D3 chart has a hard row ceiling — SVG stops painting past a few thousand nodes. The cap is applied via applyChartCap() and the badge via drawRowCapBadge() — both exported so you can use them outside the built-in charts.
| Chart | Cap | Why |
|---|---|---|
| Table widget | 10 | Dashboard context, not a paginated grid |
| Bubble, Circle packing | 500 | SVG circle count + hover handlers |
| Parallel coordinates | 1 000 | One polyline per row |
| Heatmap | 2 000 | Cell count = rows × cols |
| Contour | 5 000 | Density estimation cost |
import { applyChartCap, drawRowCapBadge, ChartErrorBoundary } from "@duckviz/widgets";
const { rows, total, capped } = applyChartCap(allRows, { max: 2000 });
if (capped) drawRowCapBadge(svgGroup, { shown: rows.length, total });<ChartErrorBoundary> wraps any chart subtree and renders a small inline fallback on render throws. Reset key is computed from (query, type, rowCount) so editing the SQL or swapping viz type retries automatically — used internally by @duckviz/dashboard@0.7.1+, safe to use yourself when rendering charts outside the <Dashboard> grid.
Chart utility functions
Shared utilities for building D3 charts:
import {
CHART_COLORS, // Default color palette (theme-aware)
toNum, // Safe number coercion
toStr, // Safe string coercion
fmtNum, // Format numbers (1234 → "1.2K")
createChartTooltip, // Create a tooltip div
positionTooltip, // Position tooltip relative to container
appendLegend, // Add a color legend to a chart
LEGEND_WIDTH, // Legend reserved width (px)
buildXTickFormatter, // Build a tick formatter for X axis
cullXTicks, // Remove overlapping X axis labels
applyChartCap, // Truncate rows to chart's hard limit
drawRowCapBadge, // Render the "Showing N of M" badge
useD3Container, // React hook for D3 container ref
} from "@duckviz/widgets";All theme-token lookups (CHART_COLORS, axis fill, subtle text) resolve via getComputedStyle(document.documentElement) — hex values are only a last-resort fallback. Don't pass raw var(--token) strings into D3 color scales; D3 can't parse them and charts render black.
fmtNum(n)
Formats numbers for chart labels:
fmtNum(1234); // "1.2K"
fmtNum(1500000); // "1.5M"
fmtNum(0.456); // "0.46"
fmtNum(42); // "42"useD3Container()
React hook that returns a ref and dimensions for D3 charts:
const { containerRef, width, height } = useD3Container();Chart categories
The registry includes 80+ chart types across 15 categories. See the full chart type reference for the complete list.
| Category | Examples |
|---|---|
| Bar | bar, grouped-bar, stacked-bar, horizontal-bar, diverging-bar |
| Line | line, multi-line, area, stacked-area, step |
| Scatter | scatter, bubble, hexbin, contour |
| Pie | pie, donut, waffle |
| Distribution | histogram, box-plot, violin, ridgeline |
| Heatmap | heatmap, calendar-heatmap |
| Hierarchy | treemap, sunburst, circle-packing |
| Network | force-directed, chord, sankey, arc-diagram |
| Radial | radial-bar, radar, gauge |
| Text | word-cloud, table |
| Specialty | candlestick, gantt, timeline, bullet, lollipop, dumbbell, bump-chart |