A set of linked charts over one shared dataset that filter each other. It has a headline, a sentence of instructions with filter chips, a row of figures that recompute from the filtered rows, two to four charts and an optional detail table. It opens on selections the author chooses.
Data table
A restrained editorial table from rows and a column config: aligned tabular numbers, an optional in-cell bar, row highlight and click-to-sort headers.
- Use when
- When readers need to look up exact values, or compare a handful of entities across several measures with different units. Sort it by the column the caption refers to and highlight the row it names.
- Avoid when
- Showing a trend or a single comparison (use line chart or bar chart); more than about 7 columns or 30 rows without
maxHeight.
Preview
| Denmark | 67% | 23.1 | +1.2% |
|---|---|---|---|
| Portugal | 46% | 22.9 | +2.7% |
| Germany | 43% | 209.4 | +3.1% |
| Spain | 42% | 112.8 | +4.4% |
| Netherlands | 41% | 49.6 | +5.8% |
| Poland | 24% | 40.2 | −0.4% |
| France | 14% | 73.5 | +0.9% |
import { DataTable } from "insightcharts";
export function Example() {
return (
<DataTable
caption="Wind and solar supplied 67% of Denmark's electricity in 2024"
subtitle="Electricity generation by country, 2024"
source="Ember (illustrative figures)"
highlight="Denmark"
sort={{ key: "share", direction: "desc" }}
columns={[
{ key: "country", label: "Country" },
{
key: "share",
label: "Wind and solar share",
format: "percent",
bar: true,
width: "38%",
},
{ key: "twh", label: "Generation, TWh", format: "decimal:1" },
{
key: "change",
label: "Change vs 2023",
format: { style: "percent", signDisplay: "exceptZero", maximumFractionDigits: 1 },
},
]}
rows={[
{ country: "Germany", share: 0.43, twh: 209.4, change: 0.031 },
{ country: "Spain", share: 0.42, twh: 112.8, change: 0.044 },
{ country: "Denmark", share: 0.67, twh: 23.1, change: 0.012 },
{ country: "Netherlands", share: 0.41, twh: 49.6, change: 0.058 },
{ country: "Portugal", share: 0.46, twh: 22.9, change: 0.027 },
{ country: "France", share: 0.14, twh: 73.5, change: 0.009 },
{ country: "Poland", share: 0.24, twh: 40.2, change: -0.004 },
]}
/>
);
}{
"type": "data-table",
"caption": "Wind and solar supplied 67% of Denmark's electricity in 2024",
"subtitle": "Electricity generation by country, 2024",
"source": "Ember (illustrative figures)",
"highlight": "Denmark",
"sort": { "key": "share", "direction": "desc" },
"columns": [
{ "key": "country", "label": "Country" },
{
"key": "share",
"label": "Wind and solar share",
"format": "percent",
"bar": true,
"width": "38%"
},
{ "key": "twh", "label": "Generation, TWh", "format": "decimal:1" },
{
"key": "change",
"label": "Change vs 2023",
"format": { "style": "percent", "signDisplay": "exceptZero", "maximumFractionDigits": 1 }
}
],
"rows": [
{ "country": "Germany", "share": 0.43, "twh": 209.4, "change": 0.031 },
{ "country": "Spain", "share": 0.42, "twh": 112.8, "change": 0.044 },
{ "country": "Denmark", "share": 0.67, "twh": 23.1, "change": 0.012 },
{ "country": "Netherlands", "share": 0.41, "twh": 49.6, "change": 0.058 },
{ "country": "Portugal", "share": 0.46, "twh": 22.9, "change": 0.027 },
{ "country": "France", "share": 0.14, "twh": 73.5, "change": 0.009 },
{ "country": "Poland", "share": 0.24, "twh": 40.2, "change": -0.004 }
]
}Render with import { Insight } from "insightcharts" and <Insight spec={spec} />. Nested specs resolve by name; nothing else to import.
Composition
Used in
1 template uses data table.
Templates
1Installation
$ pnpm dlx shadcn@latest add @insightcharts/data-tableThe CLI copies the source into your project and needs the @insightcharts registry in components.json; see installation. With the package, import it by name:
import { DataTable } from "insightcharts";Examples
Scrolling, sticky header, compact
| Vienna | 1.98 | 2015 | 71.2 | 31% |
|---|---|---|---|---|
| Copenhagen | 0.66 | 2012 | 78.5 | 49% |
| Amsterdam | 0.93 | 2014 | 75.1 | 38% |
| Zurich | 0.44 | 2011 | 80.3 | 27% |
| Helsinki | 0.67 | 2016 | 69.8 | 22% |
| Munich | 1.51 | 2018 | 66.4 | 19% |
| Oslo | 0.72 | 2010 | 82.6 | 41% |
| Stockholm | 0.98 | 2013 | 77.0 | 35% |
| Lyon | 0.52 | 2019 | 61.3 | – |
| Ghent | 0.27 | 2017 | 64.9 | 30% |
| Bologna | 0.39 | 2020 | 58.7 | 12% |
| Ljubljana | 0.30 | 2009 | 73.4 | 16% |
import { DataTable } from "insightcharts";
export function Example() {
return (
<DataTable
caption="Twelve cities with a low-traffic centre"
subtitle="Population in millions, liveability score out of 100, share of trips by bike"
density="compact"
maxHeight={260}
columns={[
{ key: "city", label: "City" },
{ key: "population", label: "Population, m", format: "decimal:2" },
{ key: "startYear", label: "Scheme began" },
{ key: "score", label: "Score", format: "decimal:1" },
{ key: "cycling", label: "Cycling share", format: "percent" },
]}
rows={[
{
city: "Vienna",
population: 1.98,
startYear: 2015,
score: 71.2,
cycling: 0.31,
},
{
city: "Copenhagen",
population: 0.66,
startYear: 2012,
score: 78.5,
cycling: 0.49,
},
{
city: "Amsterdam",
population: 0.93,
startYear: 2014,
score: 75.1,
cycling: 0.38,
},
{
city: "Zurich",
population: 0.44,
startYear: 2011,
score: 80.3,
cycling: 0.27,
},
{
city: "Helsinki",
population: 0.67,
startYear: 2016,
score: 69.8,
cycling: 0.22,
},
{
city: "Munich",
population: 1.51,
startYear: 2018,
score: 66.4,
cycling: 0.19,
},
{ city: "Oslo", population: 0.72, startYear: 2010, score: 82.6, cycling: 0.41 },
{
city: "Stockholm",
population: 0.98,
startYear: 2013,
score: 77,
cycling: 0.35,
},
{ city: "Lyon", population: 0.52, startYear: 2019, score: 61.3, cycling: null },
{ city: "Ghent", population: 0.27, startYear: 2017, score: 64.9, cycling: 0.3 },
{
city: "Bologna",
population: 0.39,
startYear: 2020,
score: 58.7,
cycling: 0.12,
},
{
city: "Ljubljana",
population: 0.3,
startYear: 2009,
score: 73.4,
cycling: 0.16,
},
]}
source="Illustrative data for the gallery"
/>
);
}{
"type": "data-table",
"caption": "Twelve cities with a low-traffic centre",
"subtitle": "Population in millions, liveability score out of 100, share of trips by bike",
"density": "compact",
"maxHeight": 260,
"columns": [
{ "key": "city", "label": "City" },
{ "key": "population", "label": "Population, m", "format": "decimal:2" },
{ "key": "startYear", "label": "Scheme began" },
{ "key": "score", "label": "Score", "format": "decimal:1" },
{ "key": "cycling", "label": "Cycling share", "format": "percent" }
],
"rows": [
{
"city": "Vienna",
"population": 1.98,
"startYear": 2015,
"score": 71.2,
"cycling": 0.31
},
{
"city": "Copenhagen",
"population": 0.66,
"startYear": 2012,
"score": 78.5,
"cycling": 0.49
},
{
"city": "Amsterdam",
"population": 0.93,
"startYear": 2014,
"score": 75.1,
"cycling": 0.38
},
{
"city": "Zurich",
"population": 0.44,
"startYear": 2011,
"score": 80.3,
"cycling": 0.27
},
{
"city": "Helsinki",
"population": 0.67,
"startYear": 2016,
"score": 69.8,
"cycling": 0.22
},
{
"city": "Munich",
"population": 1.51,
"startYear": 2018,
"score": 66.4,
"cycling": 0.19
},
{
"city": "Oslo",
"population": 0.72,
"startYear": 2010,
"score": 82.6,
"cycling": 0.41
},
{
"city": "Stockholm",
"population": 0.98,
"startYear": 2013,
"score": 77,
"cycling": 0.35
},
{
"city": "Lyon",
"population": 0.52,
"startYear": 2019,
"score": 61.3,
"cycling": null
},
{
"city": "Ghent",
"population": 0.27,
"startYear": 2017,
"score": 64.9,
"cycling": 0.3
},
{
"city": "Bologna",
"population": 0.39,
"startYear": 2020,
"score": 58.7,
"cycling": 0.12
},
{
"city": "Ljubljana",
"population": 0.3,
"startYear": 2009,
"score": 73.4,
"cycling": 0.16
}
],
"source": "Illustrative data for the gallery"
}Render with import { Insight } from "insightcharts" and <Insight spec={spec} />. Nested specs resolve by name; nothing else to import.
Default columns, no sorting
| Plan | Seats | Price per seat | Annual discount |
|---|---|---|---|
| Starter | 5 | $8 | None |
| Team | 25 | $12 | 10% |
| Enterprise | 250 | Custom | Negotiated |
import { DataTable } from "insightcharts";
export function Example() {
return (
<DataTable
sortable={false}
rows={[
{ Plan: "Starter", Seats: 5, "Price per seat": "$8", "Annual discount": "None" },
{ Plan: "Team", Seats: 25, "Price per seat": "$12", "Annual discount": "10%" },
{
Plan: "Enterprise",
Seats: 250,
"Price per seat": "Custom",
"Annual discount": "Negotiated",
},
]}
/>
);
}{
"type": "data-table",
"sortable": false,
"rows": [
{ "Plan": "Starter", "Seats": 5, "Price per seat": "$8", "Annual discount": "None" },
{ "Plan": "Team", "Seats": 25, "Price per seat": "$12", "Annual discount": "10%" },
{
"Plan": "Enterprise",
"Seats": 250,
"Price per seat": "Custom",
"Annual discount": "Negotiated"
}
]
}Render with import { Insight } from "insightcharts" and <Insight spec={spec} />. Nested specs resolve by name; nothing else to import.
Two highlighted rows, negative values
| North | €4.8M | +6.4% |
|---|---|---|
| South | €4M | −2.1% |
| East | €2.2M | −4.8% |
| West | €5.1M | −0.7% |
| Islands | €640K | +11.2% |
import { DataTable } from "insightcharts";
export function Example() {
return (
<DataTable
caption="Revenue grew in 2 of 5 regions in the fourth quarter"
highlight={["North", "Islands"]}
columns={[
{ key: "region", label: "Region" },
{ key: "revenue", label: "Revenue", format: "currency:EUR" },
{
key: "growth",
label: "Quarterly growth",
format: { style: "percent", signDisplay: "exceptZero", maximumFractionDigits: 1 },
bar: true,
width: "40%",
},
]}
rows={[
{ region: "North", revenue: 4820000, growth: 0.064 },
{ region: "South", revenue: 3950000, growth: -0.021 },
{ region: "East", revenue: 2210000, growth: -0.048 },
{ region: "West", revenue: 5130000, growth: -0.007 },
{ region: "Islands", revenue: 640000, growth: 0.112 },
]}
/>
);
}{
"type": "data-table",
"caption": "Revenue grew in 2 of 5 regions in the fourth quarter",
"highlight": ["North", "Islands"],
"columns": [
{ "key": "region", "label": "Region" },
{ "key": "revenue", "label": "Revenue", "format": "currency:EUR" },
{
"key": "growth",
"label": "Quarterly growth",
"format": { "style": "percent", "signDisplay": "exceptZero", "maximumFractionDigits": 1 },
"bar": true,
"width": "40%"
}
],
"rows": [
{ "region": "North", "revenue": 4820000, "growth": 0.064 },
{ "region": "South", "revenue": 3950000, "growth": -0.021 },
{ "region": "East", "revenue": 2210000, "growth": -0.048 },
{ "region": "West", "revenue": 5130000, "growth": -0.007 },
{ "region": "Islands", "revenue": 640000, "growth": 0.112 }
]
}Render with import { Insight } from "insightcharts" and <Insight spec={spec} />. Nested specs resolve by name; nothing else to import.
Props
| Prop | Type | Default | Description |
|---|---|---|---|
| rowsrequired | Row[] | One object per row. | |
| columns | (string | { key, label?, format?, align?, bar?, sortable?, width? })[] | Columns in order. Defaults to every key of the first row. Numeric columns are right-aligned. | |
| caption | string | The table's title, as a real <caption>. | |
| subtitle | string | Units and period, under the caption. | |
| source | string | Shown under the table as "Source: ...". | |
| highlight | string | number | (string | number)[] | Rows to emphasise, matched on the first column (or highlightKey). | |
| highlightKey | string | Field highlight matches against. | |
| sort | { key, direction?: "asc" | "desc" } | Initial sort. | |
| sortable | boolean | true | Click or press Enter on a header to sort; a third press restores the original order. |
| maxHeight | number | string | Height before the body scrolls under the sticky header. | |
| density | "comfortable" | "compact" | "comfortable" | Row padding. |