Two states of one measure side by side, as figures or small charts, with the change between them stated in the middle.
Bar chart
Compares a value across categories with bars from a zero baseline.
- Use when
- Ranking or comparing up to about 20 categories; grouped for a few series, stacked when the total and its composition both matter.
- Avoid when
- Change over many time points (use line chart); two values per category (use dumbbell chart); more than about 5 stacked segments.
Preview
Renewables supplied 98% of Norway's electricity in 2023
import { BarChart } from "insightcharts";
const data = [
{ country: "Norway", share: 98 },
{ country: "Brazil", share: 89 },
{ country: "Canada", share: 68 },
];
export function Example() {
return (
<BarChart
data={data}
x="country"
y="share"
format={"{value}%"}
title="Renewables supplied 98% of Norway's electricity in 2023"
/>
);
}{
"type": "bar-chart",
"data": [
{ "country": "Norway", "share": 98 },
{ "country": "Brazil", "share": 89 },
{ "country": "Canada", "share": 68 }
],
"x": "country",
"y": "share",
"format": "{value}%",
"title": "Renewables supplied 98% of Norway's electricity in 2023"
}Render with import { Insight } from "insightcharts" and <Insight spec={spec} />. Nested specs resolve by name; nothing else to import.
Composition
Used in
8 molecules and 10 templates use bar chart.
Molecules
8A chart with a callout beside it that states the main fact. In narrow containers the callout goes underneath.
One chart frame with a segmented control that switches between several views of the same data, morphing between views that share a chart type.
Two to four charts over one shared dataset in a responsive grid: clicking a mark in one filters the others, with the active filters shown as removable chips.
The sheet an infographic is composed on. It paints a full-bleed ground from a poster theme, forces one theme and colour mode on everything inside, and lays out a masthead, a grid of panels and a footer. It can save the whole sheet as one PNG.
A scroll-driven story from JSON: one base chart spec and a list of steps, each with narration and a patch of chart props applied while that step is active.
A story section: a numbered step marker or a headline, introductory prose, then one or more exhibits in a responsive one- to three-column grid.
Repeats any chart spec once per value of a facet field, each panel with its own filtered rows and a title, in a grid that follows the container width.
Templates
10A compared with B on one page. It has a headline that states the result, the headline figures mirrored either side of their labels, one hero exhibit that shows both sides, paired exhibits on a shared scale, a verdict callout and a source note.
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.
A one-page summary. It has a headline that states the main finding with its number, a deck, a row of three or four key figures, one hero chart with a takeaway callout, two smaller supporting exhibits, a short list of actions and a source note.
A vertical infographic in one narrow column. It opens with a big number or a proportion statement (hook), then the hero exhibit, then numbered sections with one exhibit each, a conclusion with an optional link, and sources.
A section of an analytical report laid out as a document. It has a title block, a summary, numbered findings, then method, limitations and sources. Each finding has one exhibit, an observation and a separately labelled interpretation with a confidence rating and a caveat.
A recurring report. It has a masthead with the report name and period, a headline that states the main change, a KPI grid with changes and sparklines, a ranked list beside one or two trend charts, an optional comparison table, short notes and a source note.
A graphic that states one fact, at a fixed aspect ratio (1:1, 4:5, 16:9 or 9:16). It has a kicker, a headline, one exhibit, a source and a credit, painted on the theme surface. A button downloads the whole card as a PNG.
A sequence of full-frame cards (title, stat, chart, quote, closing) that the reader swipes or steps through. Each card states one fact. It has an optional fixed aspect ratio, a keyboard-accessible pager and a stacked layout for print.
A story told in date order. It has a headline, one overview exhibit of the whole period (a timeline chart or an annotated line), dated chapters along a rail, a closing paragraph and a source note. Each chapter has a short text and an optional small exhibit.
A long article read by scrolling. It has a headline, then an ordered list of blocks in a reading column, then a source note. The blocks are prose, figures, charts with a callout, scrolly sequences, pull quotes, stat rows, small multiples and chapter breaks. Exhibits can be wider than the column.
Installation
$ pnpm dlx shadcn@latest add @insightcharts/bar-chartThe 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 { BarChart } from "insightcharts";Examples
Two bars, horizontal
46% of renters under 35 moved home in the past year
Share who moved home in the past 12 months
import { BarChart } from "insightcharts";
const data = [
{ group: "Renters under 35", share: 46 },
{ group: "Owners under 35", share: 21 },
];
export function Example() {
return (
<BarChart
data={data}
x="group"
y="share"
format={"{value}%"}
title="46% of renters under 35 moved home in the past year"
subtitle="Share who moved home in the past 12 months"
note="Illustrative figures."
/>
);
}{
"type": "bar-chart",
"data": [
{ "group": "Renters under 35", "share": 46 },
{ "group": "Owners under 35", "share": 21 }
],
"x": "group",
"y": "share",
"format": "{value}%",
"title": "46% of renters under 35 moved home in the past year",
"subtitle": "Share who moved home in the past 12 months",
"note": "Illustrative figures."
}Render with import { Insight } from "insightcharts" and <Insight spec={spec} />. Nested specs resolve by name; nothing else to import.
Vertical over time with a target line
The world added 452 GW of solar capacity in 2024
New solar capacity added worldwide per year, gigawatts
import { BarChart } from "insightcharts";
const data = [
{ year: "2019", gw: 98 },
{ year: "2020", gw: 127 },
{ year: "2021", gw: 168 },
{ year: "2022", gw: 239 },
{ year: "2023", gw: 407 },
{ year: "2024", gw: 452 },
];
export function Example() {
return (
<BarChart
data={data}
x="year"
y="gw"
sort="none"
orientation="vertical"
format={"{value} GW"}
annotations={[
{ type: "line", axis: "y", value: 300, label: "Forecast" },
{ type: "band", axis: "x", from: "2020", to: "2021", label: "Pandemic" },
]}
title="The world added 452 GW of solar capacity in 2024"
subtitle="New solar capacity added worldwide per year, gigawatts"
note="Illustrative figures."
/>
);
}{
"type": "bar-chart",
"data": [
{ "year": "2019", "gw": 98 },
{ "year": "2020", "gw": 127 },
{ "year": "2021", "gw": 168 },
{ "year": "2022", "gw": 239 },
{ "year": "2023", "gw": 407 },
{ "year": "2024", "gw": 452 }
],
"x": "year",
"y": "gw",
"sort": "none",
"orientation": "vertical",
"format": "{value} GW",
"annotations": [
{ "type": "line", "axis": "y", "value": 300, "label": "Forecast" },
{ "type": "band", "axis": "x", "from": "2020", "to": "2021", "label": "Pandemic" }
],
"title": "The world added 452 GW of solar capacity in 2024",
"subtitle": "New solar capacity added worldwide per year, gigawatts",
"note": "Illustrative figures."
}Render with import { Insight } from "insightcharts" and <Insight spec={spec} />. Nested specs resolve by name; nothing else to import.
Grouped, horizontal, highlighted
Electricity access in South Asia rose from 58% in 2000 to 99% in 2023
Share of population with access to electricity
import { BarChart } from "insightcharts";
const data = [
{ region: "Sub-Saharan Africa", y2000: 26, y2023: 51 },
{ region: "South Asia", y2000: 58, y2023: 99 },
{ region: "Latin America", y2000: 92, y2023: 98 },
{ region: "East Asia and Pacific", y2000: 88, y2023: 99 },
];
export function Example() {
return (
<BarChart
data={data}
x="region"
y={["y2000", "y2023"]}
names={{ y2000: "2000", y2023: "2023" }}
highlight="y2023"
format={"{value}%"}
title="Electricity access in South Asia rose from 58% in 2000 to 99% in 2023"
subtitle="Share of population with access to electricity"
source="World Bank (rounded)"
note="Figures are approximate, for illustration"
/>
);
}{
"type": "bar-chart",
"data": [
{ "region": "Sub-Saharan Africa", "y2000": 26, "y2023": 51 },
{ "region": "South Asia", "y2000": 58, "y2023": 99 },
{ "region": "Latin America", "y2000": 92, "y2023": 98 },
{ "region": "East Asia and Pacific", "y2000": 88, "y2023": 99 }
],
"x": "region",
"y": ["y2000", "y2023"],
"names": { "y2000": "2000", "y2023": "2023" },
"highlight": "y2023",
"format": "{value}%",
"title": "Electricity access in South Asia rose from 58% in 2000 to 99% in 2023",
"subtitle": "Share of population with access to electricity",
"source": "World Bank (rounded)",
"note": "Figures are approximate, for illustration"
}Render with import { Insight } from "insightcharts" and <Insight spec={spec} />. Nested specs resolve by name; nothing else to import.
Stacked to 100%
71% of adults aged 18 to 29 watch short-form video daily
How often adults watch short-form video, by age group
import { BarChart } from "insightcharts";
const data = [
{ age: "18 to 29", Daily: 71, Weekly: 19, Rarely: 10 },
{ age: "30 to 49", Daily: 58, Weekly: 25, Rarely: 17 },
{ age: "50 to 64", Daily: 39, Weekly: 28, Rarely: 33 },
{ age: "65 and over", Daily: 22, Weekly: 24, Rarely: 54 },
];
export function Example() {
return (
<BarChart
data={data}
x="age"
y={["Daily", "Weekly", "Rarely"]}
stack="percent"
orientation="horizontal"
title="71% of adults aged 18 to 29 watch short-form video daily"
subtitle="How often adults watch short-form video, by age group"
note="Illustrative figures."
/>
);
}{
"type": "bar-chart",
"data": [
{ "age": "18 to 29", "Daily": 71, "Weekly": 19, "Rarely": 10 },
{ "age": "30 to 49", "Daily": 58, "Weekly": 25, "Rarely": 17 },
{ "age": "50 to 64", "Daily": 39, "Weekly": 28, "Rarely": 33 },
{ "age": "65 and over", "Daily": 22, "Weekly": 24, "Rarely": 54 }
],
"x": "age",
"y": ["Daily", "Weekly", "Rarely"],
"stack": "percent",
"orientation": "horizontal",
"title": "71% of adults aged 18 to 29 watch short-form video daily",
"subtitle": "How often adults watch short-form video, by age group",
"note": "Illustrative figures."
}Render with import { Insight } from "insightcharts" and <Insight spec={spec} />. Nested specs resolve by name; nothing else to import.
Forced vertical with names too long for their bands
46% of commuters travel to work by private car
Main mode of travel to work, share of commuters
import { BarChart } from "insightcharts";
const data = [
{ mode: "Private car", share: 46 },
{ mode: "Public transport", share: 24 },
{ mode: "Walking", share: 15 },
{ mode: "Cycling and scooters", share: 9 },
{ mode: "Taxi and ride-hailing", share: 4 },
{ mode: "Other", share: 2 },
];
export function Example() {
return (
<BarChart
data={data}
x="mode"
y="share"
orientation="vertical"
format={"{value}%"}
height={260}
title="46% of commuters travel to work by private car"
subtitle="Main mode of travel to work, share of commuters"
note="Illustrative figures. Long category labels wrap, then rotate."
/>
);
}{
"type": "bar-chart",
"data": [
{ "mode": "Private car", "share": 46 },
{ "mode": "Public transport", "share": 24 },
{ "mode": "Walking", "share": 15 },
{ "mode": "Cycling and scooters", "share": 9 },
{ "mode": "Taxi and ride-hailing", "share": 4 },
{ "mode": "Other", "share": 2 }
],
"x": "mode",
"y": "share",
"orientation": "vertical",
"format": "{value}%",
"height": 260,
"title": "46% of commuters travel to work by private car",
"subtitle": "Main mode of travel to work, share of commuters",
"note": "Illustrative figures. Long category labels wrap, then rotate."
}Render with import { Insight } from "insightcharts" and <Insight spec={spec} />. Nested specs resolve by name; nothing else to import.
Stress: 18 long category names
64% of professional, scientific and technical staff work from home at least one day a week
Share of employees working from home at least one day a week, by sector
import { BarChart } from "insightcharts";
const data = [
{ sector: "Professional, scientific and technical services", share: 64 },
{ sector: "Information and communication", share: 63 },
{ sector: "Financial and insurance activities", share: 62 },
{ sector: "Public administration and defence", share: 55 },
{ sector: "Education", share: 54 },
{ sector: "Real estate activities", share: 48 },
{ sector: "Administrative and support services", share: 47 },
{ sector: "Arts, entertainment and recreation", share: 46 },
{ sector: "Wholesale and retail trade", share: 39 },
{ sector: "Manufacturing", share: 38 },
{ sector: "Electricity, gas, steam and air conditioning supply", share: 32 },
{ sector: "Construction", share: 31 },
{ sector: "Human health and social work", share: 30 },
{ sector: "Transportation and storage", share: 23 },
{ sector: "Mining and quarrying", share: 22 },
{ sector: "Water supply, sewerage and waste management", share: 16 },
{ sector: "Agriculture, forestry and fishing", share: 15 },
{ sector: "Accommodation and food services", share: 14 },
];
export function Example() {
return (
<BarChart
data={data}
x="sector"
y="share"
labels
format={"{value}%"}
title="64% of professional, scientific and technical staff work from home at least one day a week"
subtitle="Share of employees working from home at least one day a week, by sector"
note="Illustrative figures."
/>
);
}{
"type": "bar-chart",
"data": [
{ "sector": "Professional, scientific and technical services", "share": 64 },
{ "sector": "Information and communication", "share": 63 },
{ "sector": "Financial and insurance activities", "share": 62 },
{ "sector": "Public administration and defence", "share": 55 },
{ "sector": "Education", "share": 54 },
{ "sector": "Real estate activities", "share": 48 },
{ "sector": "Administrative and support services", "share": 47 },
{ "sector": "Arts, entertainment and recreation", "share": 46 },
{ "sector": "Wholesale and retail trade", "share": 39 },
{ "sector": "Manufacturing", "share": 38 },
{ "sector": "Electricity, gas, steam and air conditioning supply", "share": 32 },
{ "sector": "Construction", "share": 31 },
{ "sector": "Human health and social work", "share": 30 },
{ "sector": "Transportation and storage", "share": 23 },
{ "sector": "Mining and quarrying", "share": 22 },
{ "sector": "Water supply, sewerage and waste management", "share": 16 },
{ "sector": "Agriculture, forestry and fishing", "share": 15 },
{ "sector": "Accommodation and food services", "share": 14 }
],
"x": "sector",
"y": "share",
"labels": true,
"format": "{value}%",
"title": "64% of professional, scientific and technical staff work from home at least one day a week",
"subtitle": "Share of employees working from home at least one day a week, by sector",
"note": "Illustrative figures."
}Render with import { Insight } from "insightcharts" and <Insight spec={spec} />. Nested specs resolve by name; nothing else to import.
Props
| Prop | Type | Default | Description |
|---|---|---|---|
| xrequired | string | Category field. | |
| yrequired | string | string[] | Value field, or several for grouped / stacked bars. | |
| series | string | Field that splits rows into series (long data). | |
| orientation | "auto" | "horizontal" | "vertical" | "auto" | Horizontal for rankings and long labels, vertical for time. |
| sort | "none" | "asc" | "desc" | Bar order. Default: descending for rankings, data order for time. | |
| stack | boolean | "percent" | false | Stack series; percent normalises to 100%. |
| labels | boolean | Value labels at bar ends. Default: on for one series of up to 12 bars. | |
| names | Record<string, string> | Display names for series keys. |
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>. |