Compares a value across categories with bars from a zero baseline.
Scrolly chart
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.
- Use when
- Walking a reader through one chart in stages: show everything, highlight the subject, change the measure, add the annotation. Make ONE change per step (the highlight, the data or the encoding) so the reader can see what changed.
- Avoid when
- Steps that change several things at once, or that swap to an unrelated chart (start a new section instead). More than about seven steps. A fact that one view can show (use chart with callout). In documents that will be printed or embedded in a short frame, set
scrollMode: "steps".
Preview
EU electricity generation by source, 2015
Share of EU electricity generation, %
In 2015 nuclear and coal together supplied 51.7% of the EU's electricity.
Coal supplied 24.6% in 2015. Only nuclear supplied more.
In 2024 coal supplied 9.8%.
Solar supplied 11.1% in 2024. It supplied 2.9% in 2015.
import { ScrollyChart, registerAll } from "insightcharts";
// The slot props below are JSON specs. This makes their `type` names resolvable.
registerAll();
export function Example() {
return (
<ScrollyChart
chart={{
type: "bar-chart",
data: [
{ source: "Coal", y2015: 24.6, y2024: 9.8 },
{ source: "Gas", y2015: 15.8, y2024: 15.7 },
{ source: "Nuclear", y2015: 27.1, y2024: 23.7 },
{ source: "Wind", y2015: 9.2, y2024: 17.4 },
{ source: "Hydro", y2015: 12, y2024: 13.2 },
{ source: "Solar", y2015: 2.9, y2024: 11.1 },
],
x: "source",
y: "y2015",
sort: "none",
orientation: "horizontal",
format: "{value}%",
height: 300,
title: "EU electricity generation by source, 2015",
subtitle: "Share of EU electricity generation, %",
source: "Ember, European Electricity Review 2025",
note: "Rounded, approximate figures for illustration",
}}
steps={[
{
id: "baseline",
label: "2015",
content: "In 2015 **nuclear and coal** together supplied 51.7% of the EU's electricity.",
},
{
id: "coal",
content: "Coal supplied 24.6% in 2015. Only nuclear supplied more.",
highlight: "Coal",
},
{
id: "today",
label: "2024",
content: "In 2024 coal supplied 9.8%.",
highlight: "Coal",
y: "y2024",
title: "EU electricity generation by source, 2024",
},
{
id: "solar",
content: "**Solar** supplied 11.1% in 2024. It supplied 2.9% in 2015.",
highlight: "Solar",
y: "y2024",
title: "Solar supplied 11.1% of EU electricity in 2024",
},
]}
stepHeight="70vh"
stickyTop={16}
/>
);
}{
"type": "scrolly-chart",
"chart": {
"type": "bar-chart",
"data": [
{ "source": "Coal", "y2015": 24.6, "y2024": 9.8 },
{ "source": "Gas", "y2015": 15.8, "y2024": 15.7 },
{ "source": "Nuclear", "y2015": 27.1, "y2024": 23.7 },
{ "source": "Wind", "y2015": 9.2, "y2024": 17.4 },
{ "source": "Hydro", "y2015": 12, "y2024": 13.2 },
{ "source": "Solar", "y2015": 2.9, "y2024": 11.1 }
],
"x": "source",
"y": "y2015",
"sort": "none",
"orientation": "horizontal",
"format": "{value}%",
"height": 300,
"title": "EU electricity generation by source, 2015",
"subtitle": "Share of EU electricity generation, %",
"source": "Ember, European Electricity Review 2025",
"note": "Rounded, approximate figures for illustration"
},
"steps": [
{
"id": "baseline",
"label": "2015",
"content": "In 2015 **nuclear and coal** together supplied 51.7% of the EU's electricity."
},
{
"id": "coal",
"content": "Coal supplied 24.6% in 2015. Only nuclear supplied more.",
"highlight": "Coal"
},
{
"id": "today",
"label": "2024",
"content": "In 2024 coal supplied 9.8%.",
"highlight": "Coal",
"y": "y2024",
"title": "EU electricity generation by source, 2024"
},
{
"id": "solar",
"content": "**Solar** supplied 11.1% in 2024. It supplied 2.9% in 2015.",
"highlight": "Solar",
"y": "y2024",
"title": "Solar supplied 11.1% of EU electricity in 2024"
}
],
"stepHeight": "70vh",
"stickyTop": 16
}Render with import { Insight } from "insightcharts" and <Insight spec={spec} />. Nested specs resolve by name; nothing else to import.
Composition
Built from
The pieces scrolly chart renders or expects in its slots.
Atoms
2A readable block of narration at a 45-75 character measure, for the text between charts.
Used in
1 template uses scrolly chart.
Templates
1A 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/scrolly-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 { ScrollyChart } from "insightcharts";Examples
Steps mode (buttons), cumulative patches
EU electricity generation by source, 2015
Share of EU electricity generation, %
The chart shows each source's share in 2015.
import { ScrollyChart, registerAll } from "insightcharts";
// The slot props below are JSON specs. This makes their `type` names resolvable.
registerAll();
export function Example() {
return (
<ScrollyChart
scrollMode="steps"
cumulative
chart={{
type: "bar-chart",
data: [
{ source: "Coal", y2015: 24.6, y2024: 9.8 },
{ source: "Gas", y2015: 15.8, y2024: 15.7 },
{ source: "Nuclear", y2015: 27.1, y2024: 23.7 },
{ source: "Wind", y2015: 9.2, y2024: 17.4 },
{ source: "Hydro", y2015: 12, y2024: 13.2 },
{ source: "Solar", y2015: 2.9, y2024: 11.1 },
],
x: "source",
y: "y2015",
sort: "none",
orientation: "horizontal",
format: "{value}%",
height: 300,
title: "EU electricity generation by source, 2015",
subtitle: "Share of EU electricity generation, %",
source: "Ember, European Electricity Review 2025",
note: "Rounded, approximate figures for illustration",
}}
steps={[
{ content: "The chart shows each source's share in 2015." },
{
content: "**Coal** is highlighted. It supplied 24.6% in 2015.",
highlight: "Coal",
},
{
content: "The chart now shows 2024. Coal is still highlighted because the patches accumulate.",
y: "y2024",
title: "EU electricity generation by source, 2024",
},
{
content: "The highlight is removed. The sources are sorted by their 2024 share.",
highlight: null,
sort: "desc",
},
]}
/>
);
}{
"type": "scrolly-chart",
"scrollMode": "steps",
"cumulative": true,
"chart": {
"type": "bar-chart",
"data": [
{ "source": "Coal", "y2015": 24.6, "y2024": 9.8 },
{ "source": "Gas", "y2015": 15.8, "y2024": 15.7 },
{ "source": "Nuclear", "y2015": 27.1, "y2024": 23.7 },
{ "source": "Wind", "y2015": 9.2, "y2024": 17.4 },
{ "source": "Hydro", "y2015": 12, "y2024": 13.2 },
{ "source": "Solar", "y2015": 2.9, "y2024": 11.1 }
],
"x": "source",
"y": "y2015",
"sort": "none",
"orientation": "horizontal",
"format": "{value}%",
"height": 300,
"title": "EU electricity generation by source, 2015",
"subtitle": "Share of EU electricity generation, %",
"source": "Ember, European Electricity Review 2025",
"note": "Rounded, approximate figures for illustration"
},
"steps": [
{ "content": "The chart shows each source's share in 2015." },
{
"content": "**Coal** is highlighted. It supplied 24.6% in 2015.",
"highlight": "Coal"
},
{
"content": "The chart now shows 2024. Coal is still highlighted because the patches accumulate.",
"y": "y2024",
"title": "EU electricity generation by source, 2024"
},
{
"content": "The highlight is removed. The sources are sorted by their 2024 share.",
"highlight": null,
"sort": "desc"
}
]
}Render with import { Insight } from "insightcharts" and <Insight spec={spec} />. Nested specs resolve by name; nothing else to import.
Swipe mode
EU electricity generation by source, 2015
Share of EU electricity generation, %
In 2015 nuclear and coal together supplied 51.7% of the EU's electricity.
import { ScrollyChart, registerAll } from "insightcharts";
// The slot props below are JSON specs. This makes their `type` names resolvable.
registerAll();
export function Example() {
return (
<ScrollyChart
scrollMode="swipe"
chart={{
type: "bar-chart",
data: [
{ source: "Coal", y2015: 24.6, y2024: 9.8 },
{ source: "Gas", y2015: 15.8, y2024: 15.7 },
{ source: "Nuclear", y2015: 27.1, y2024: 23.7 },
{ source: "Wind", y2015: 9.2, y2024: 17.4 },
{ source: "Hydro", y2015: 12, y2024: 13.2 },
{ source: "Solar", y2015: 2.9, y2024: 11.1 },
],
x: "source",
y: "y2015",
sort: "none",
orientation: "horizontal",
format: "{value}%",
height: 300,
title: "EU electricity generation by source, 2015",
subtitle: "Share of EU electricity generation, %",
source: "Ember, European Electricity Review 2025",
note: "Rounded, approximate figures for illustration",
}}
steps={[
{
id: "baseline",
label: "2015",
content: "In 2015 **nuclear and coal** together supplied 51.7% of the EU's electricity.",
},
{
id: "coal",
content: "Coal supplied 24.6% in 2015. Only nuclear supplied more.",
highlight: "Coal",
},
{
id: "today",
label: "2024",
content: "In 2024 coal supplied 9.8%.",
highlight: "Coal",
y: "y2024",
title: "EU electricity generation by source, 2024",
},
{
id: "solar",
content: "**Solar** supplied 11.1% in 2024. It supplied 2.9% in 2015.",
highlight: "Solar",
y: "y2024",
title: "Solar supplied 11.1% of EU electricity in 2024",
},
]}
/>
);
}{
"type": "scrolly-chart",
"scrollMode": "swipe",
"chart": {
"type": "bar-chart",
"data": [
{ "source": "Coal", "y2015": 24.6, "y2024": 9.8 },
{ "source": "Gas", "y2015": 15.8, "y2024": 15.7 },
{ "source": "Nuclear", "y2015": 27.1, "y2024": 23.7 },
{ "source": "Wind", "y2015": 9.2, "y2024": 17.4 },
{ "source": "Hydro", "y2015": 12, "y2024": 13.2 },
{ "source": "Solar", "y2015": 2.9, "y2024": 11.1 }
],
"x": "source",
"y": "y2015",
"sort": "none",
"orientation": "horizontal",
"format": "{value}%",
"height": 300,
"title": "EU electricity generation by source, 2015",
"subtitle": "Share of EU electricity generation, %",
"source": "Ember, European Electricity Review 2025",
"note": "Rounded, approximate figures for illustration"
},
"steps": [
{
"id": "baseline",
"label": "2015",
"content": "In 2015 **nuclear and coal** together supplied 51.7% of the EU's electricity."
},
{
"id": "coal",
"content": "Coal supplied 24.6% in 2015. Only nuclear supplied more.",
"highlight": "Coal"
},
{
"id": "today",
"label": "2024",
"content": "In 2024 coal supplied 9.8%.",
"highlight": "Coal",
"y": "y2024",
"title": "EU electricity generation by source, 2024"
},
{
"id": "solar",
"content": "**Solar** supplied 11.1% in 2024. It supplied 2.9% in 2015.",
"highlight": "Solar",
"y": "y2024",
"title": "Solar supplied 11.1% of EU electricity in 2024"
}
]
}Render with import { Insight } from "insightcharts" and <Insight spec={spec} />. Nested specs resolve by name; nothing else to import.
Props
| Prop | Type | Default | Description |
|---|---|---|---|
| chartrequired | InsightSpec | The base chart spec: { type, data, x, y, ... }. Give it a title; steps may replace it. | |
| stepsrequired | { content, id?, label?, ...chart props }[] | The steps in order. content is a short paragraph that says plainly what the chart now shows (**bold** works). Every other key is a chart prop merged over chart for that step: highlight, transform, annotations, x, y, title… Use null to remove a base prop. Change one thing per step. | |
| cumulative | boolean | false | Each step's patch builds on the previous steps instead of on the base spec alone. |
| scrollMode | "auto" | "scroll" | "swipe" | "steps" | "auto" | scroll pins the chart while steps scroll past; swipe makes steps swipeable cards; steps shows previous/next buttons; auto uses scroll, or swipe on narrow touch screens. (Named scrollMode because mode is the colour mode.) |
| side | "left" | "right" | "left" | Side of the text column in scroll mode. Narrow containers overlay the text on the chart. |
| stickyTop | number | string | 0 | Offset from the top of the viewport at which the chart pins; set it to the height of a fixed header. |
| stepHeight | string | "80vh" | Scroll distance per step, as a CSS length. |
| onStepChange | (index) => void | Called when the active step changes. | |
| title | string | Heading above the whole story. | |
| subtitle | string | Line under the heading. | |
| source | string | Shown after the last step as "Source: ...". Prefer the chart's own source, which stays visible. | |
| note | string | Method note or caveat. | |
| theme | ThemeInput | Theme override, passed on to the chart and the step cards. | |
| mode | "auto" | "system" | "light" | "dark" | "auto" | Colour mode. |