A poster that counts at scale. Blocks of thousands of unit icons fill most of the sheet, a second band counts reference groups with the same icon at the same size in a muted colour, and a bottom row holds three small graphics: squares sized by area with a ratio bracket, thick bars with the numbers inside, and shares as nested outlines or filled silhouettes.
Unit blocks
Shows the size of each group as a solid block of small icons, one icon per fixed number of units.
- Use when
- Large counts of people or things in 2 to 12 groups, where the reader should see the crowd itself: 20,350 marathon finishers by age group, one figure per 10 runners.
- Avoid when
- Precise comparison of close values (use block bars or bar chart); fewer than about 30 icons per group (use pictogram chart or icon array grid); shares of one whole (use waffle chart).
Preview
import { UnitBlocks } from "insightcharts";
const data = [
{ age: "Under 30", finishers: 4180 },
{ age: "30 to 39", finishers: 6920 },
{ age: "40 to 49", finishers: 5340 },
{ age: "50 to 59", finishers: 2760 },
{ age: "60 and over", finishers: 1150 },
];
export function Example() {
return (
<UnitBlocks
data={data}
name="age"
value="finishers"
unit={10}
unitLabel="10 finishers"
format="integer"
title="6,920 of the 20,350 finishers were aged 30 to 39"
subtitle="Finishers of the Lakeshore Marathon by age group, 2025"
source="Illustrative data"
note="Invented figures for a fictional race"
/>
);
}{
"type": "unit-blocks",
"data": [
{ "age": "Under 30", "finishers": 4180 },
{ "age": "30 to 39", "finishers": 6920 },
{ "age": "40 to 49", "finishers": 5340 },
{ "age": "50 to 59", "finishers": 2760 },
{ "age": "60 and over", "finishers": 1150 }
],
"name": "age",
"value": "finishers",
"unit": 10,
"unitLabel": "10 finishers",
"format": "integer",
"title": "6,920 of the 20,350 finishers were aged 30 to 39",
"subtitle": "Finishers of the Lakeshore Marathon by age group, 2025",
"source": "Illustrative data",
"note": "Invented figures for a fictional race"
}Render with import { Insight } from "insightcharts" and <Insight spec={spec} />. Nested specs resolve by name; nothing else to import.
Composition
Used in
1 infographic uses unit blocks.
Infographics
1Installation
$ pnpm dlx shadcn@latest add @insightcharts/unit-blocksThe 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 { UnitBlocks } from "insightcharts";Examples
Highlight, click to select
import { UnitBlocks } from "insightcharts";
const data = [
{ age: "Under 30", finishers: 4180 },
{ age: "30 to 39", finishers: 6920 },
{ age: "40 to 49", finishers: 5340 },
{ age: "50 to 59", finishers: 2760 },
{ age: "60 and over", finishers: 1150 },
];
export function Example() {
return (
<UnitBlocks
data={data}
name="age"
value="finishers"
unit={10}
unitLabel="10 finishers"
format="integer"
highlight="60 and over"
select
title="1,150 finishers were aged 60 or over"
subtitle="Finishers of the Lakeshore Marathon by age group, 2025"
source="Illustrative data"
note="Invented figures for a fictional race"
/>
);
}{
"type": "unit-blocks",
"data": [
{ "age": "Under 30", "finishers": 4180 },
{ "age": "30 to 39", "finishers": 6920 },
{ "age": "40 to 49", "finishers": 5340 },
{ "age": "50 to 59", "finishers": 2760 },
{ "age": "60 and over", "finishers": 1150 }
],
"name": "age",
"value": "finishers",
"unit": 10,
"unitLabel": "10 finishers",
"format": "integer",
"highlight": "60 and over",
"select": true,
"title": "1,150 finishers were aged 60 or over",
"subtitle": "Finishers of the Lakeshore Marathon by age group, 2025",
"source": "Illustrative data",
"note": "Invented figures for a fictional race"
}Render with import { Insight } from "insightcharts" and <Insight spec={spec} />. Nested specs resolve by name; nothing else to import.
Stress: 20,350 icons, one per finisher
import { UnitBlocks } from "insightcharts";
const data = [
{ age: "Under 30", finishers: 4180 },
{ age: "30 to 39", finishers: 6920 },
{ age: "40 to 49", finishers: 5340 },
{ age: "50 to 59", finishers: 2760 },
{ age: "60 and over", finishers: 1150 },
];
export function Example() {
return (
<UnitBlocks
data={data}
name="age"
value="finishers"
unit={1}
unitLabel="1 finisher"
format="integer"
title="20,350 people finished the Lakeshore Marathon in 2025"
subtitle="Finishers by age group. Every figure is one runner"
source="Illustrative data"
note="Invented figures for a fictional race"
/>
);
}{
"type": "unit-blocks",
"data": [
{ "age": "Under 30", "finishers": 4180 },
{ "age": "30 to 39", "finishers": 6920 },
{ "age": "40 to 49", "finishers": 5340 },
{ "age": "50 to 59", "finishers": 2760 },
{ "age": "60 and over", "finishers": 1150 }
],
"name": "age",
"value": "finishers",
"unit": 1,
"unitLabel": "1 finisher",
"format": "integer",
"title": "20,350 people finished the Lakeshore Marathon in 2025",
"subtitle": "Finishers by age group. Every figure is one runner",
"source": "Illustrative data",
"note": "Invented figures for a fictional race"
}Render with import { Insight } from "insightcharts" and <Insight spec={spec} />. Nested specs resolve by name; nothing else to import.
Columns of squares, coloured by a series field
import { UnitBlocks } from "insightcharts";
const data = [
{ depot: "Northgate", buses: 412, fuel: "Electric" },
{ depot: "Harbour Road", buses: 268, fuel: "Electric" },
{ depot: "Millbrook", buses: 655, fuel: "Diesel" },
{ depot: "Eastfield", buses: 381, fuel: "Diesel" },
{ depot: "Quarry Lane", buses: 97, fuel: "Hydrogen" },
];
export function Example() {
return (
<UnitBlocks
data={data}
name="depot"
value="buses"
series="fuel"
unit={1}
unitLabel="1 bus"
icon="square"
layout="columns"
title="Millbrook depot ran 655 diesel buses in 2025"
subtitle="Buses in service by depot and fuel, 2025"
source="Illustrative data"
note="Invented figures for a fictional operator"
/>
);
}{
"type": "unit-blocks",
"data": [
{ "depot": "Northgate", "buses": 412, "fuel": "Electric" },
{ "depot": "Harbour Road", "buses": 268, "fuel": "Electric" },
{ "depot": "Millbrook", "buses": 655, "fuel": "Diesel" },
{ "depot": "Eastfield", "buses": 381, "fuel": "Diesel" },
{ "depot": "Quarry Lane", "buses": 97, "fuel": "Hydrogen" }
],
"name": "depot",
"value": "buses",
"series": "fuel",
"unit": 1,
"unitLabel": "1 bus",
"icon": "square",
"layout": "columns",
"title": "Millbrook depot ran 655 diesel buses in 2025",
"subtitle": "Buses in service by depot and fuel, 2025",
"source": "Illustrative data",
"note": "Invented figures for a fictional operator"
}Render with import { Insight } from "insightcharts" and <Insight spec={spec} />. Nested specs resolve by name; nothing else to import.
Stress: twelve uneven groups, long names
import { UnitBlocks } from "insightcharts";
const data = [
{ species: "Common starling", birds: 9840 },
{ species: "House sparrow", birds: 7120 },
{ species: "Wood pigeon", birds: 5310 },
{ species: "Blackbird", birds: 3980 },
{ species: "Blue tit", birds: 3460 },
{ species: "European goldfinch", birds: 2210 },
{ species: "Great spotted woodpecker", birds: 640 },
{ species: "Long-tailed tit", birds: 580 },
{ species: "Eurasian sparrowhawk", birds: 140 },
{ species: "Common kingfisher", birds: 90 },
{ species: "Lesser spotted woodpecker", birds: 40 },
{ species: "Hawfinch", birds: 20 },
];
export function Example() {
return (
<UnitBlocks
data={data}
name="species"
value="birds"
unit={20}
unitLabel="20 birds"
icon="circle"
format="integer"
title="Volunteers counted 9,840 starlings in one weekend in 2025"
subtitle="Birds recorded in one weekend garden count by species, 2025"
source="Illustrative data"
note="Invented figures for a fictional survey"
/>
);
}{
"type": "unit-blocks",
"data": [
{ "species": "Common starling", "birds": 9840 },
{ "species": "House sparrow", "birds": 7120 },
{ "species": "Wood pigeon", "birds": 5310 },
{ "species": "Blackbird", "birds": 3980 },
{ "species": "Blue tit", "birds": 3460 },
{ "species": "European goldfinch", "birds": 2210 },
{ "species": "Great spotted woodpecker", "birds": 640 },
{ "species": "Long-tailed tit", "birds": 580 },
{ "species": "Eurasian sparrowhawk", "birds": 140 },
{ "species": "Common kingfisher", "birds": 90 },
{ "species": "Lesser spotted woodpecker", "birds": 40 },
{ "species": "Hawfinch", "birds": 20 }
],
"name": "species",
"value": "birds",
"unit": 20,
"unitLabel": "20 birds",
"icon": "circle",
"format": "integer",
"title": "Volunteers counted 9,840 starlings in one weekend in 2025",
"subtitle": "Birds recorded in one weekend garden count by species, 2025",
"source": "Illustrative data",
"note": "Invented figures for a fictional survey"
}Render with import { Insight } from "insightcharts" and <Insight spec={spec} />. Nested specs resolve by name; nothing else to import.
Props
| Prop | Type | Default | Description |
|---|---|---|---|
| namerequired | string | Group field: one block of icons per row of data. | |
| valuerequired | string | Value field: the quantity each block counts out. | |
| unit | number | Units per icon. Default: a round number that gives about 2,000 icons in total. | |
| unitLabel | string | What one icon stands for, for the key line, e.g. "10 finishers". | |
| icon | IconName | "path://..." | "person" | Built-in icon name or a custom single SVG path. |
| series | string | Field that colours the blocks. Default: one colour per group, up to eight groups. | |
| layout | "packed" | "columns" | "packed" | packed: equal block heights, widths follow the counts. columns: equal widths, blocks grow upwards from a baseline. |
| columns | number | Blocks per line in the columns layout. Default: all, two on a phone. | |
| iconSize | number | Icon height in px. Default: the largest that fits, chosen per chart. To put two charts on one scale, give both the same unit and iconSize: the icon size, the gaps and the worth of an icon are then identical and the chart takes the height it needs. | |
| sort | "none" | "asc" | "desc" | "none" | Order of the blocks. |
Common chart props 29
Every chart built with defineChart accepts these on top of its own props.
| Prop | Type | Description |
|---|---|---|
| data | DataInput | Rows, CSV text, a URL, { url, path }, an async function, or an Arrow-style table. Omit inside <InsightData>. |
| transform | Transform | Transform[] | A pipeline of transforms applied before drawing. |
| title | string | The title. One plain statement of what the data shows, with the number and the period ("Monthly churn fell from 4.2% to 2.1% in 2024"), or a plain label ("Monthly churn, 2024"). One clause, no figures of speech. |
| subtitle | string | Measure, unit, population, period. |
| source | string | Data source, shown as "Source: ...". |
| note | string | Method notes or caveats. |
| height | number | string | CSS height of the plot. Default 360. |
| aspect | number | Width / height. Overrides height. |
| highlight | string | string[] | The entity (or entities) the title is about. Every other series is drawn in grey. |
| annotations | Annotation[] | Reference lines, shaded bands and callouts, in data coordinates. |
| format | FormatSpec | How to print values: "compact", "percent", "currency:USD", "{value} kg", Intl options, or a function. |
| legend | boolean | Show the legend. Default: shown when there are two or more series. |
| select | boolean | string | Make marks clickable to filter. Inside <InsightData> the selection filters every other chart. Pass a field name, or true to use the chart's category field. |
| onSelect | (values: string[], field: string) => void | Called with the selected values whenever the selection changes. |
| option | EChartsOption | ((option: EChartsOption, ctx: BuildContext) => EChartsOption) | Deep-merged over the generated ECharts option, or a function that receives and returns it. Use it for ECharts settings no prop covers. |
| theme | ThemeInput | A theme, or a partial set of tokens merged over the surrounding theme, for this chart only. |
| mode | ModeInput | "auto" (default) follows the page, "system" follows the OS, or force "light" / "dark". |
| renderer | RendererInput | "auto" (default) uses SVG for light charts and canvas for heavy ones. |
| animate | boolean | Set false to disable animation. Reduced-motion preferences are always respected. |
| lazy | boolean | Mount the chart only when it scrolls near the viewport. Default true. |
| group | string | Link tooltips with other charts sharing this group id. |
| exportable | boolean | ExportFormat[] | Show an export menu: true or a list of formats from png, jpeg, svg, pdf, csv, json. |
| table | boolean | Show a collapsible "View data" table under the chart. |
| alt | string | Accessible description: the chart type and measures, then the main pattern and the notable values. |
| id | string | Stable id for this chart's selection in the shared store. |
| onEvents | Record<string, ChartEventHandler> | ECharts event handlers, e.g. { click: (params, chart) => {} }. Prefix a name with zr: to bind to the canvas itself. |
| onReady | (chart: EChartsInstance) => void | Called with the ECharts instance once it is created. |
| className | string | Class name for the outer <figure>. |
| style | CSSProperties | Inline style for the outer <figure>. |