Embedding Dashboards
Embed interactive DuckViz dashboards in any React application.
The <Dashboard> component from @duckviz/dashboard renders a draggable grid of charts powered by DuckDB SQL queries. This guide shows how to embed it in your app.
Basic setup
import { DuckvizDBProvider, useDuckDB } from "@duckviz/db";
import { Dashboard, type DashboardConfig } from "@duckviz/dashboard";
import { DuckvizThemeProvider } from "@duckviz/ui";
function App() {
return (
<DuckvizThemeProvider preset="terracotta" mode="dark">
<DuckvizDBProvider persistence>
<DataLoader />
</DuckvizDBProvider>
</DuckvizThemeProvider>
);
}Building a DashboardConfig
A dashboard config describes what widgets to render and how to lay them out:
const config: DashboardConfig = {
name: "Sales Overview",
widgets: [
{
id: "revenue-by-category",
type: "bar",
title: "Revenue by Category",
description: "Top 10 categories by total revenue",
dataKey: `
SELECT category, SUM(amount) AS value
FROM t_sales
GROUP BY category
ORDER BY value DESC
LIMIT 10
`,
layout: { x: 0, y: 0, w: 6, h: 8 },
},
{
id: "monthly-trend",
type: "line",
title: "Monthly Revenue",
description: "Revenue trend over the past 12 months",
dataKey: `
SELECT strftime(date, '%Y-%m') AS category, SUM(amount) AS value
FROM t_sales
GROUP BY 1
ORDER BY 1
`,
layout: { x: 6, y: 0, w: 6, h: 8 },
},
{
id: "region-distribution",
type: "pie",
title: "Revenue by Region",
description: "Distribution across regions",
dataKey: `
SELECT region AS category, SUM(amount) AS value
FROM t_sales
GROUP BY region
`,
layout: { x: 0, y: 8, w: 4, h: 8 },
},
],
};Layout coordinates
Each widget has a layout with grid coordinates:
x— column position (0–11 in a 12-column grid)y— row position (0-based)w— width in columnsh— height in grid rows (each row is 30px by default)
If no layout is specified, widgets are auto-arranged.
Rendering the dashboard
function SalesDashboard() {
return (
<div style={{ height: "100vh", padding: 16 }}>
<Dashboard config={config} />
</div>
);
}The Dashboard auto-wires to the nearest DuckvizDBProvider to execute SQL queries. No manual data fetching is needed.
Auto-ingesting data
If your rows come from an API (not a user-uploaded file), pass them as datasets and Dashboard will ingest into DuckDB before rendering widgets:
import type { DuckvizDataset } from "@duckviz/db";
const datasets: DuckvizDataset[] = [
{ name: "Sales", data: salesRows, tableName: "t_sales" },
];
<Dashboard config={config} datasets={datasets} />The widget SQL queries reference the derived table names (t_sales here). Tables are dropped on unmount unless you pass dropTablesOnUnmount={false}.
Using widgets from Explorer
When users generate widgets in the Explorer, convert them to WidgetConfig:
import type { RecommendedWidget } from "@duckviz/explorer";
import type { DashboardConfig, WidgetConfig } from "@duckviz/dashboard";
function buildConfig(widgets: RecommendedWidget[]): DashboardConfig {
return {
name: "My Dashboard",
widgets: widgets.map((w, i) => ({
id: `w-${i}`,
type: w.type,
title: w.title,
description: w.description,
dataKey: w.duckdbQuery,
config: w.config,
})),
};
}Custom widget actions
Add toolbar buttons (refresh, delete, etc.) to each widget:
<Dashboard
config={config}
renderWidgetActions={(ctx) => (
<div style={{ display: "flex", gap: 4 }}>
<button onClick={() => ctx.refresh()} title="Refresh">
↻
</button>
<button onClick={() => removeWidget(ctx.widgetId)} title="Delete">
✕
</button>
</div>
)}
/>Forcing a layout rebuild
When you add or remove widgets, bump the layoutVersion prop to trigger a clean layout rebuild:
const [layoutVersion, setLayoutVersion] = useState(0);
function addWidget(widget: WidgetConfig) {
setWidgets((prev) => [...prev, widget]);
setLayoutVersion((v) => v + 1); // Force rebuild
}
<Dashboard config={config} layoutVersion={layoutVersion} />Custom empty state
Show a custom message when the dashboard has no widgets:
<Dashboard
config={{ ...config, widgets: [] }}
emptyState={
<div style={{ textAlign: "center", padding: "4rem" }}>
<h3>No widgets yet</h3>
<p>Go to the Explorer and click + on a chart to add it here.</p>
</div>
}
/>Grid configuration
Customize the grid layout:
const config: DashboardConfig = {
widgets: [...],
grid: {
cols: 12, // Number of columns (default: 12)
rowHeight: 30, // Pixel height per grid row (default: 30)
margin: [8, 8], // Gap between widgets [horizontal, vertical] (default: [8, 8])
},
};Theming
The Dashboard reads CSS custom properties from DuckvizThemeProvider. You can also pass a ThemeConfig directly:
<Dashboard
config={{
...config,
theme: {
mode: "dark",
variables: {
"--app-surface-primary": "#1a1a2e",
"--app-text-primary": "#e0e0e0",
},
},
}}
/>See the Custom Themes guide for the full list of theme variables.