Building a dashboard
A layout that reads top to bottom (filters, headline numbers, the trend, breakdowns, then the rows) with every view linked through one selection.
Layout
Start from the questions people ask in order: what is filtered, how are we doing, how did we get here, where is it coming from, and which rows. One answer per row of the page:
The code
Each step is one component. The selection does the wiring: brushing the daily bars filters the KPIs, the list and the table; clicking a region filters the daily bars; and the FilterBar shows a chip for both, so a reader can see and clear every filter in one place.
import {
Card, DataTable, FilterBar, KPIGroup, Histogram, BarList, QuartileProvider, Selection, dataset,
} from '@quartile/react';
import '@quartile/react/styles.css';
// Your application supplies a comparable amount for each record.
type Order = {
id: string; date: string; region: string; channel: string; product: string;
amount: number; previous_amount: number;
};
export function Overview({ rows, periodLabel }: { rows: Order[]; periodLabel: string }) {
// 1. Describe the data once.
const orders = dataset(rows, {
amount: { label: 'Revenue', format: 'currency' },
previous_amount: { label: 'Previous', format: 'currency' },
});
return (
<QuartileProvider theme="system">
{/* 2. One selection links every view below. */}
<Selection id="orders">
{/* 3. Filters first: chips for the fields people filter by, and for every brush or click. */}
<FilterBar data={orders} fields={[{ field: 'region' }, { field: 'channel' }]} />
{/* 4. The headline numbers, each with its comparison. */}
<KPIGroup
data={orders}
items={[
{ label: 'Revenue', value: 'amount', compareValue: 'previous_amount', trendBy: 'date', format: 'currency-compact' },
{ label: 'Orders', aggregate: 'count', trendBy: 'date' },
]}
/>
{/* 5. The trend, which publishes a date range. */}
<Card title="Revenue per day" subtitle={periodLabel}>
<Histogram data={orders} x="date" value="amount" interval="day" brush aria-label="Revenue per day" />
</Card>
{/* 6. Breakdowns that publish what you click. */}
<Card title="Revenue by region">
<BarList data={orders} category="region" value="amount" select />
</Card>
{/* 7. The rows behind it all. */}
<DataTable
data={orders}
title="Top products"
groupBy="product"
defaultSort="-revenue"
pageSize={8}
columns={[
{ field: 'product' },
{ key: 'revenue', field: 'amount', label: 'Revenue', aggregate: 'sum', cell: 'bar' },
{ key: 'trend', field: 'amount', label: 'Trend', cell: 'sparkline', over: 'date' },
]}
/>
</Selection>
</QuartileProvider>
);
}The storefront example
The gallery's storefront example is a complete dashboard built only from @quartile/react: a date range picker, a FilterBar, a KPI group, a revenue chart with a comparison, regions, a channel mix, an orders-by-hour heatmap and a product table, all on one selection, with light and dark themes.
Checklist
- Declare labels and formats once with
dataset(); every axis, tooltip, table and summary uses them. - Say the period in each card's subtitle, and the comparison in each KPI.
- Choose which views publish. A brush on the trend and clicks on one or two breakdowns are usually enough; too many publishers make filters hard to follow.
- Give every chart an
aria-labeland offer the table view where numbers matter. See Accessible charts. - Filtering runs in memory over the rows you pass. Use bounded server results or the optional worker path for larger sources, and measure your workload; see Performance.