Skip to content
InsightCharts

Search

Search guides and components

Core concepts

Stories

Scrolly keeps one graphic in place and changes its state as the reader scrolls, swipes or steps through a list of text steps.

Scrolly takes a list of steps and a render function. Each step has its text in content, plus any other keys you choose. Those keys hold the state the graphic should be in at that step. Because the same chart instance receives each new state, ECharts morphs between them.

Scroll mode

The graphic pins while the steps scroll past. Scroll this page slowly.

Renewable generation, TWh

2020, 2022 and 2024 combined

Illustrative data
Renewable generation in three regions, grouped by source. Hydro generated 11,520 TWh, 53% of the total.
Solar generated 3,870 TWh, 18% of the total.
The same rows grouped by region.
Asia generated 10,590 TWh, 49% of the total.

The step crossing the middle of the viewport is the active one. In a container narrower than 768 pixels, like this column, the text cards pass over the graphic instead of sitting beside it; the home page shows the side-by-side layout. The source of this demo:

import { BarChart, Scrolly } from "insightcharts";

// Each step changes one thing: the grouping field or the highlight.
const steps = [
  { id: "sources", x: "source", highlight: undefined, content: "Renewable generation in three regions, grouped by source. Hydro generated 11,520 TWh, 53% of the total." },
  { id: "solar", x: "source", highlight: "Solar", content: "Solar generated 3,870 TWh, 18% of the total." },
  { id: "regions", x: "region", highlight: undefined, content: "The same rows grouped by region." },
  { id: "asia", x: "region", highlight: "Asia", content: "Asia generated 10,590 TWh, 49% of the total." },
];

export default function ScrollStory({ stickyTop = 72, stepHeight = "70vh" }: { stickyTop?: number; stepHeight?: string }) {
  return (
    <Scrolly steps={steps} mode="scroll" stickyTop={stickyTop} stepHeight={stepHeight}>
      {({ step }) => (
        <BarChart
          data="/data/energy.csv"
          x={step.x}
          y="twh"
          transform={{ type: "aggregate", groupBy: step.x, measures: { twh: { op: "sum", field: "twh" } } }}
          highlight={step.highlight}
          sort="desc"
          orientation="horizontal"
          format="compact"
          height={280}
          title="Renewable generation, TWh"
          subtitle="2020, 2022 and 2024 combined"
          note="Illustrative data"
        />
      )}
    </Scrolly>
  );
}

Steps and swipe modes

mode="steps" shows one card with previous and next buttons, and needs no scrolling room: use it inside dashboards, slides and cards. mode="swipe" turns the steps into cards that swipe horizontally under the graphic. In both, the left and right arrow keys move between steps when focus is inside the story.

Monthly visits by channel

Thousands of visits per month

Illustrative data
Search visits rose from 42,000 in January to 53,000 in June.
ModeBehaviour
"auto" (default)"swipe" on touch devices in a container under 640 pixels wide, "scroll" otherwise.
"scroll"Sticky graphic, steps scroll past.
"swipe"Horizontal scroll-snapped cards, with previous and next buttons.
"steps"One card at a time, buttons only.

Writing steps

const steps = [
  { id: "all", content: "Hydro generated 11,520 TWh, 53% of the total.", x: "source" },
  { id: "solar", content: "Solar generated 3,870 TWh, 18% of the total.", x: "source", highlight: "Solar" },
  { id: "region", content: "Asia generated 10,590 TWh, 49% of the total.", x: "region", highlight: "Asia" },
];

<Scrolly steps={steps}>
  {({ step, index, progress, count }) => <BarChart x={step.x} highlight={step.highlight} ... />}
</Scrolly>
  • Change one thing per step: the data, the highlight, or the encoding.
  • content is one or two plain sentences that state what the chart shows at that step, with the number from the data. It follows the same rules as a title; see writing titles. It accepts any React node.
  • id is rendered as the step's element id, so #solar deep-links to it.
  • Return the same component from the render function at every step where you want a morph. Swapping to a different component remounts the chart.

The render function receives step, index, count and progress, a number from 0 to 1 through the active step. progress updates in scroll mode only and is 0 elsewhere.

Props

PropDefaultPurpose
stepsrequiredThe steps. Each needs content; other keys are yours.
childrenrequired(state) => ReactNode, the graphic for the active step.
mode"auto"See above.
side"left"Which side the text column sits on in scroll mode.
stickyTop0Distance from the top of the viewport at which the graphic pins. Set it to the height of a sticky header.
stepHeight"80vh"Scroll distance per step, as a CSS length.
indexuncontrolledControlled active step.
onStepChangeCalled with (index, step).
theme, colorModeSame as theme and mode on charts.

Accessibility and fallbacks

All of the step text is in the document. In scroll mode every step is a <section> in reading order, so the text can be read with scripting or animation off. Steps mode announces the active card through a polite live region. Charts skip their transitions when the reader prefers reduced motion.