ChartGraph is used for displaying various types of graphs plotted on an x and y axis. It makes it easier to identify and understand patterns over time across different categories, enabling people to make informed decisions quickly.

also known as Bar Graph, Line Graph, Column Graph

ChartGraph is still under development. The component API, style, and behaviour might change in follow-up releases.
Figma:

Responsive:

Adaptive:

Props

Component props
Name
Type
Default
accessibilityLabel
Required
string
-

Label to provide more context around ChartGraph’s content.

See the accessibility guidelines on ARIA attributes to learn more.

data
Required
ReadonlyArray<{
  // @ts-expect-error - TS2411 - Property "name" of type "string | number" is not assignable to "string" index type "number".
  name: string | number;
  [key: string]: number;
}>
-

The source data, in which each element is an object. Each object must specify a "name" associated to each category (string value) or timestamp (numberic value) in time series charts.

The additional key-values represent one or more series of data presented on ChartGraph for each category or timestamp. A sequence of source data objects generate one or more series of data across categories or timestamps.

elements
Required
ReadonlyArray<{
  axis?: "left" | "right" | "bottom" | "top";
  color?: "01" | "02" | "03" | "04" | "05" | "06" | "07" | "08" | "09" | "10" | "11" | "12";
  id: string;
  precision?: "exact" | "estimate";
  type: "line" | "bar";
}>
-

The series elements, bars or lines, of the ChartGraph.

See the combo variants, color variant, layout, color variant, precision in line graphs variant to learn more about configuring bars and lines.

onVisualPatternChange
Required
() => void
-

Callback fired when the Accessibility IconButton in ChartGraph is clicked. ChartGraph's visual patterns is a controlled feature. onVisualPatternChange is used to enable/disable visual patterns in ChartGraph.

See the accessibility guidelines on visual patterns to learn more.

title
Required
string
-

Title of ChartGraph. Be sure to localize the text.

See the header variant to learn more.

visualPatternSelected
Required
"disabled" | "default" | "visualPattern"
-

ChartGraph's visual patterns is a controlled feature. visualPatternSelected manages visual patterns in ChartGraph. When ChartGraph represents a standalone data series that don't require comparison, the Accessibility IconButton can be hidden with "disabled".

See the accessibility guidelines on visual patterns to learn more.

children
React.Node
-

Must be instances of TagData or TileData

See the selector variant to learn more.

description
string
-

Description of ChartGraph. Be sure to localize the text.

See the header variant to learn more.

helpButton
ReactElement
-

HelpButton to be placed after the title for to provide supplemental support to the user. See the header variant to learn more.

initialTicks
"auto" | 3
"auto"

ChartGraph is responsive. If your ChartGraph's width is below the 576 px breakpoint, ChartGraph will flick and correct the amount of ticks. To prevent that, set initialTicks to 3. ChartGraph above 576 px, don't require this adjusment.

See the color variant for implementation guidance.

labelMap
{
  [key: string]: string;
}
-

Replaces the labels from data in default tooltips, legends, and axis. Use for lacalization.

See the localizations section to learn more.

layout
"horizontal" | "vertical" | "horizontalBiaxial" | "verticalBiaxial"
"vertical"

Sets the horizontal or vertical layout of bars and lines and the single or double axis in the chart.

See the layout variant to learn more.

legend
"auto" | "none"
"auto"

Displays data about the datasets that are appearing on the chart.

See the legend variant to learn more.

modalZIndex
Indexable
-

An object representing the zIndex value of the tabular representation modal. Learn more about zIndex classes

range
[
    number | "auto" | "dataMin" | "dataMax" | ((arg1: number) => number),
    number | "auto" | "dataMin" | "dataMax" | ((arg1: number) => number),
  ]
| {
    xAxisBottom?: [
      number | "auto" | "dataMin" | "dataMax" | ((arg1: number) => number),
      number | "auto" | "dataMin" | "dataMax" | ((arg1: number) => number),
    ];
    xAxisTop?: [
      number | "auto" | "dataMin" | "dataMax" | ((arg1: number) => number),
      number | "auto" | "dataMin" | "dataMax" | ((arg1: number) => number),
    ];
    yAxisLeft?: [
      number | "auto" | "dataMin" | "dataMax" | ((arg1: number) => number),
      number | "auto" | "dataMin" | "dataMax" | ((arg1: number) => number),
    ];
    yAxisRight?: [
      number | "auto" | "dataMin" | "dataMax" | ((arg1: number) => number),
      number | "auto" | "dataMin" | "dataMax" | ((arg1: number) => number),
    ];
  }
"[0, auto]"

The start and end values of the axis.

See the range variant to learn more.

referenceAreas
ReadonlyArray<{
  id: string;
  label: string;
  x1: string | number;
  x2: string | number;
  y1: string | number;
  y2: string | number;
  yAxisId: string;
  style?: "default";
}>
"[]"

Sets non-data visual areas in ChartGraph.

See the reference area variant to learn more.

renderTooltip
"auto"
| "none"
| ((arg1: {
    active: boolean | null | undefined;
    payload: Record<any, any> | null | undefined;
    label: string | number;
  }) => React.Node)
"auto"

Displays data about the datasets on hover over each data point.

See the tooltip variant to learn more.

stacked
boolean
-

When set to "true", bars are stacked.

See the stacked bars variant to learn more.

tickFormatter
{
  timeseries?: (arg1: number) => string | number;
  xAxisTop?: (arg1: number, arg2: number) => string | number;
  xAxisBottom?: (arg1: number, arg2: number) => string | number;
  yAxisRight?: (arg1: number, arg2: number) => string | number;
  yAxisLeft?: (arg1: number, arg2: number) => string | number;
}
-

A function for formatting ticks on the axis.

Timeseries require the 'timeseries' key for formatting dates in the axis and tooltip. The 'xAxisBottom' overrides 'timeseries' when are both present.

See the tick format variant and time series variant to learn more

titleDisplay
"visible" | "hidden"
"visible"

Whether the title should be visible or not. If hidden, the title is still available in the tabular representation modal.

type
"combo" | "line" | "bar"
"bar"

Type of chart.
See the types variant to learn more.

Usage guidelines

These are overall guidelines for ChartGraph. For usage guidelines on specific graphs, see:
- Bar graph usage guidelines
- Line graph usage guidelines
- Charts and graphs general guidelines

When to use
  • To compare data sets
  • To show trends, frequency of occurrences and distribution of data across time
  • To visually summarize and explain complex data sets and concepts
When not to use
  • To show parts-to-whole. Use a pie or donut chart instead.
  • To visualize a series of stages in a sequence that get smaller over time. Use a funnel chart instead.
  • To show large data sets that are too hard to show in a chart. Use Table instead.

Best practices

These are overall best practices for ChartGraph. For best practices on specific graphs, see:
- Bar graph best practices
- Line graph best practices
- Charts and graphs general guidelines

Do

Limit the amount of data you show in a graph so that it is readable and easy to follow.

Don't

Add a lot of data to a graph that makes it hard to read. If you need to show a lot of data, use Table instead. Another option is using multiple graphs in a grid.

Do

When displaying multiple categories in lines or bars, stick to the default color sequence provided since it has been optimized for color blindness.

Don't

Pick colors that are too similar to each other and hard to tell apart, especially for those with visual impairments.

Do

Use a biaxial chart when there is a significant difference between values. A suggestion is a 50%+ difference.

Don't

Use a biaxial chart when there is a very minor difference between values being compared. Use a chart with a single axis instead.

Accessibility

Visual patterns

Charts use color to represent discrete categories. However, people with color blindness or other visual impairments may have difficulty telling certain colors apart.

Therefore, ChartGraph provides an accessibility view mode where colors in bars and lines are replaced with visual patterns to help in their interpretation. Bar charts use pattern fills and line charts use series markers with different shapes to help distinguish between data points without using color alone. Each pattern fill and time series shape corresponds to one of the colors in our 12-color categorical palette.

ChartGraph provides visualPatternSelected and onVisualPatternChange props to manage the visual state of the component externally. If a person selects Low Vision Features in the settings or enables the visual patterns in one component, other charts can also be enabled at the same time to adapt to a user’s accessibility preferences.

ChartGraph displays an IconButton in the header section that allows to enable the visual pattern from the component itself.

Bar pattern fills
Main palette
Pattern: 01
Pattern: 02
Pattern: 03
Pattern: 04
Pattern: 05
Pattern: 06
Extended palette
Pattern: 07
Pattern: 08
Pattern: 09
Pattern: 10
Pattern: 11
Pattern: 12
Bar pattern fills example
import { useState } from 'react';
import { ChartGraph } from 'gestalt-charts';

export default function Example() {
  const [visualPatternSelected, setVisualPatternSelected] =
    useState('visualPattern');

  const data = [
    {
      name: 'A',
      Color_01: 90,
      Color_02: 90,
      Color_03: 90,
      Color_04: 90,
      Color_05: 90,
      Color_06: 90,
    },
  ];

  return (
    <ChartGraph
      accessibilityLabel="Example of chart with decal pattern in bars"
      data={data}
      elements={[
        { type: 'bar', id: 'Color_01' },
        { type: 'bar', id: 'Color_02' },
        { type: 'bar', id: 'Color_03' },
        { type: 'bar', id: 'Color_04' },
        { type: 'bar', id: 'Color_05' },
        { type: 'bar', id: 'Color_06' },
      ]}
      onVisualPatternChange={() =>
        setVisualPatternSelected((value) =>
          value === 'default' ? 'visualPattern' : 'default'
        )
      }
      range={[0, 100]}
      title="ChartGraph"
      titleDisplay="hidden"
      type="bar"
      visualPatternSelected={visualPatternSelected}
    />
  );
}

Line series markers

For line graphs, shapes are used to help tell categories apart.

Main palette
Marker: 01
Marker: 02
Marker: 03
Marker: 04
Marker: 05
Marker: 06
Extended palette
Marker: 07
Marker: 08
Marker: 09
Marker: 10
Marker: 11
Marker: 12
Line series markers example
import { useState } from 'react';
import { FixedZIndex } from 'gestalt';
import { ChartGraph } from 'gestalt-charts';

export default function Example() {
  const [visualPatternSelected, setVisualPatternSelected] =
    useState('visualPattern');

  const data = [
    {
      name: new Date(2023, 1, 1).getTime(),
      Color_01: 50,
      Color_02: 100,
      Color_03: 150,
      Color_04: 200,
      Color_05: 250,
      Color_06: 300,
    },
    {
      name: new Date(2023, 5, 15).getTime(),
      Color_01: 100,
      Color_02: 150,
      Color_03: 200,
      Color_04: 250,
      Color_05: 300,
      Color_06: 350,
    },
    {
      name: new Date(2023, 10, 1).getTime(),
      Color_01: 150,
      Color_02: 200,
      Color_03: 250,
      Color_04: 300,
      Color_05: 350,
      Color_06: 400,
    },
  ];

  return (
    <ChartGraph
      accessibilityLabel="Example of chart with decal pattern in lines"
      data={data}
      elements={[
        { type: 'line', id: 'Color_01' },
        { type: 'line', id: 'Color_02' },
        { type: 'line', id: 'Color_03' },
        { type: 'line', id: 'Color_04' },
        { type: 'line', id: 'Color_05' },
        { type: 'line', id: 'Color_06' },
      ]}
      modalZIndex={new FixedZIndex(11)}
      onVisualPatternChange={() =>
        setVisualPatternSelected((value) =>
          value === 'default' ? 'visualPattern' : 'default'
        )
      }
      range={{
        xAxisBottom: [
          new Date(2023, 0, 1).getTime(),
          new Date(2023, 11, 1).getTime(),
        ],
      }}
      tickFormatter={{
        timeseries: (date) =>
          `${new Intl.DateTimeFormat('en-US', { month: 'short' }).format(
            date
          )}-${new Intl.DateTimeFormat('en-US', { day: '2-digit' }).format(
            date
          )}`,
      }}
      title="ChartGraph"
      titleDisplay="hidden"
      type="line"
      visualPatternSelected={visualPatternSelected}
    />
  );
}

ARIA attributes

Charts are presented as images to screen readers. Provide a description using the accessibilityLabel to provide more context around ChartGraph’s content.

Don't use accessibilityLabel to describe the ChartGraph content itself. We’re working on adding a Table view feature to access the detailed data of ChartGraph in a more granular and accessible way.

accessibilityLabel is automatically prefixed with "ChartGraph." to differentiate ChartGraph from other images.

import { useState } from 'react';
import { ChartGraph } from 'gestalt-charts';

export default function Example() {
  const [visualPatternSelected, setVisualPatternSelected] = useState('default');

  const data = [
    {
      name: 'A',
      Series_01: 50,
      Series_02: 100,
    },
    {
      name: 'B',
      Series_01: 100,
      Series_02: 200,
    },
    {
      name: 'C',
      Series_01: 250,
      Series_02: 300,
    },
  ];

  return (
    <ChartGraph
      accessibilityLabel="Example of Bar chart"
      data={data}
      elements={[
        { type: 'bar', id: 'Series_01' },
        { type: 'bar', id: 'Series_02' },
      ]}
      onVisualPatternChange={() =>
        setVisualPatternSelected((value) =>
          value === 'default' ? 'visualPattern' : 'default'
        )
      }
      title="ChartGraph"
      titleDisplay="hidden"
      type="bar"
      visualPatternSelected={visualPatternSelected}
    />
  );
}

Tabular representation

An additional button is available to show chart data as a table so that it’s easier to

  • navigate with a screen reader
  • read data for those who have difficulty processing visual information
  • download data to view in a person’s own tools

The tabular data is also available to download as a .csv file.

Localization

Be sure to localize all text strings. Note that localization can lengthen text by 20 to 30 percent.

To localize data content, use the labelMap prop. See the example for detailed implementation guidance.

To localize dates in time series, use the tickFormatter.timeseries prop. See the time series example for detailed implementation guidance.

ChartGraph depends on DefaultLabelProvider for internal text strings. Localize the texts via DefaultLabelProvider. Learn more
import { useState } from 'react';
import { DefaultLabelProvider } from 'gestalt';
import { ChartGraph } from 'gestalt-charts';

export default function Example() {
  const [visualPatternSelected, setVisualPatternSelected] = useState('default');

  return (
    <DefaultLabelProvider
      labels={{
        ChartGraph: {
          accessibilityLabelPrefixText: 'ChartGraph',
          defaultViewText: 'Standard-Ansichtsmodus.',
          accessibleViewText: 'Ansichtsmodus für Barrierefreiheit.',
          tabularData: 'Tabellarische Darstellung.',
          accessibilityLabelDismissModal: 'Tabellendarstellung modal aufheben.',
          tableSeriesText: 'Reihe.',
          tableXAxisText: 'x-Achsen-Werte.',
          tableYAxisText: 'y-Achsen-Werte.',
          downloadCsvButtonText: 'Als .csv herunterladen.',
          cancelButtonText: 'Abbrechen.',
        },
      }}
    >
      <ChartGraph
        accessibilityLabel="Beispiel für ein Liniendiagramm"
        data={[
          {
            name: 'Women',
            Users: 100,
            Clickthroughs: 200,
          },
          {
            name: 'Men',
            Users: 200,
            Clickthroughs: 300,
          },
        ]}
        description="Leistung im Laufe der Zeit. Impressionen geben an, wie oft Ihr Pin auf dem Bildschirm angezeigt wurde."
        elements={[
          { type: 'bar', id: 'Users' },
          { type: 'bar', id: 'Clickthroughs' },
        ]}
        labelMap={{
          Women: 'Frauen',
          Men: 'Männer',
          Users: 'Benutzer',
          Clickthroughs: 'Durchklicken',
        }}
        onVisualPatternChange={() =>
          setVisualPatternSelected((value) =>
            value === 'default' ? 'visualPattern' : 'default'
          )
        }
        title="Eindrücke"
        type="bar"
        visualPatternSelected={visualPatternSelected}
      />
    </DefaultLabelProvider>
  );
}

Subcomponents

LegendIcon

LegendIcon should only be used within custom tooltips. See the custom tooltip variant for implementation guidance.

LegendIcon Props

LegendIcon subcomponent props
Name
Type
Default
payloadData
Required
{
    dataKey: string;
    name: string;
    stroke: string | null | undefined;
    value: number;
    strokeDasharray: string | null | undefined | number;
    strokeWidth?: number;
    color: string | null | undefined;
    fill: string | null | undefined;
    legendType?: "line" | "rect";
    isLegend?: boolean;
  }
| {
    referenceArea: "default";
    isLegend?: boolean;
  }
-

Data received from the renderTooltip.

See the [custom tooltip variant](/web/chartgraph#Tooltip) for implementation guidance.

Variants

Bar horizontal

Arrange bars in rows that stack from top to bottom when horizontal space and you have longer text labels. Also known as a "horizontal bar chart".

Props: type="bar" elements=[{..., type:'bar'}] layout="horizontal"

import { useState } from 'react';
import { Flex } from 'gestalt';
import { ChartGraph } from 'gestalt-charts';

export default function Example() {
  const [visualPatternSelected, setVisualPatternSelected] = useState('default');

  const data = [
    {
      name: 'Pacific Northwest',
      Impressions: 1000,
      CPM: 1050,
      CPC: 1000,
    },
    {
      name: 'Sunbelt',
      Impressions: 1000,
      CPM: 1500,
      CPC: 1000,
    },
    {
      name: 'Great Lakes',
      Impressions: 1000,
      CPM: 1050,
      CPC: 1000,
    },
  ];

  return (
    <Flex direction="column" gap={2} height="100%" width="100%">
      <ChartGraph
        accessibilityLabel="Example of a vertical bar chart"
        data={data}
        elements={[
          { type: 'bar', id: 'Impressions' },
          { type: 'bar', id: 'CPM' },
          { type: 'bar', id: 'CPC' },
        ]}
        layout="horizontal"
        onVisualPatternChange={() =>
          setVisualPatternSelected((value) =>
            value === 'default' ? 'visualPattern' : 'default'
          )
        }
        title="Regions in the US"
        type="bar"
        visualPatternSelected={visualPatternSelected}
      />
    </Flex>
  );
}

Bar column

In a column chart, bars are ordered horizontally. Use this when you have a small amount of data to compare and the horizontal space to do so. This includes text labels. If text labels are long, use Bar horizontal instead.

Props: type="bar" layout="horizontal"

import { useState } from 'react';
import { Flex } from 'gestalt';
import { ChartGraph } from 'gestalt-charts';

export default function Example() {
  const [visualPatternSelected, setVisualPatternSelected] = useState('default');

  const data = [
    {
      name: '10-24',
      Impressions: 1000,
      CPM: 1050,
      CPC: 1000,
    },
    {
      name: '25-50',
      Impressions: 1000,
      CPM: 1500,
      CPC: 1000,
    },
    {
      name: '50+',
      Impressions: 1000,
      CPM: 1050,
      CPC: 1000,
    },
  ];

  return (
    <Flex direction="column" gap={2} height="100%" width="100%">
      <ChartGraph
        accessibilityLabel="Example of a vertical bar chart"
        data={data}
        elements={[
          { type: 'bar', id: 'Impressions' },
          { type: 'bar', id: 'CPM' },
          { type: 'bar', id: 'CPC' },
        ]}
        onVisualPatternChange={() =>
          setVisualPatternSelected((value) =>
            value === 'default' ? 'visualPattern' : 'default'
          )
        }
        title="Age"
        type="bar"
        visualPatternSelected={visualPatternSelected}
      />
    </Flex>
  );
}

Stacked bars

Stacked bar charts break bars into smaller categories so that their relationship to the whole can be seen.

Props: type="bar" stacked={true}

import { useState } from 'react';
import { FixedZIndex, Flex } from 'gestalt';
import { ChartGraph } from 'gestalt-charts';

export default function Example() {
  const [visualPatternSelected, setVisualPatternSelected] = useState('default');

  const data = [
    {
      name: new Date(2023, 0, 1).getTime(),
      Awareness: 5000,
      Consideration: 1000,
      'Catalog sales': 4000,
      Conversions: 2500,
    },
    {
      name: new Date(2023, 1, 1).getTime(),
      Awareness: 4000,
      Consideration: 5000,
      'Catalog sales': 1000,
      Conversions: 2500,
    },
    {
      name: new Date(2023, 2, 1).getTime(),
      Awareness: 2500,
      Consideration: 5000,
      'Catalog sales': 1000,
      Conversions: 4000,
    },
    {
      name: new Date(2023, 3, 1).getTime(),
      Awareness: 2500,
      Consideration: 4000,
      'Catalog sales': 1000,
      Conversions: 5000,
    },
  ];

  return (
    <Flex direction="column" gap={2} height="100%" width="100%">
      <ChartGraph
        accessibilityLabel="Example of stacked bars chart"
        data={data}
        elements={[
          { type: 'bar', id: 'Awareness' },
          { type: 'bar', id: 'Consideration' },
          { type: 'bar', id: 'Catalog sales' },
          { type: 'bar', id: 'Conversions' },
        ]}
        modalZIndex={new FixedZIndex(11)}
        onVisualPatternChange={() =>
          setVisualPatternSelected((value) =>
            value === 'default' ? 'visualPattern' : 'default'
          )
        }
        range={{
          xAxisBottom: ['auto', 'auto'],
        }}
        stacked
        tickFormatter={{
          yAxisLeft: (value) => `${value / 1000}K`,
          timeseries: (date) =>
            `${new Intl.DateTimeFormat('en-US', { month: 'short' }).format(
              date
            )}-${new Intl.DateTimeFormat('en-US', { day: '2-digit' }).format(
              date
            )}`,
        }}
        title="508 campaigns"
        visualPatternSelected={visualPatternSelected}
      />
    </Flex>
  );
}

Line

A line graph plots numeric values for categorical data as a line that shows a progression through time.

Props: type="line" elements=[{..., type:'line'}]

import { useState } from 'react';
import { FixedZIndex, Flex } from 'gestalt';
import { ChartGraph } from 'gestalt-charts';

export default function Example() {
  const [visualPatternSelected, setVisualPatternSelected] = useState('default');

  const data = [
    {
      name: new Date(2023, 0, 1).getTime(),
      Engagement: 850000,
      Saves: 870000,
      Impressions: 280000,
      'Page visits': 5000,
    },
    {
      name: new Date(2023, 1, 2).getTime(),
      Engagement: 800000,
      Saves: 690000,
      Impressions: 790000,
      'Page visits': 9000,
    },
    {
      name: new Date(2023, 2, 3).getTime(),
      Engagement: 890000,
      Saves: 850000,
      Impressions: 250000,
      'Page visits': 8000,
    },
    {
      name: new Date(2023, 3, 4).getTime(),
      Engagement: 870000,
      Saves: 550000,
      Impressions: 230000,
      'Page visits': 7000,
    },
    {
      name: new Date(2023, 4, 5).getTime(),
      Engagement: 830000,
      Saves: 84000,
      Impressions: 710000,
      'Page visits': 3000,
    },
  ];

  return (
    <Flex direction="column" gap={2} height="100%" width="100%">
      <ChartGraph
        accessibilityLabel="Example of line chart"
        data={data}
        elements={[
          { type: 'line', id: 'Engagement' },
          { type: 'line', id: 'Saves' },
          { type: 'line', id: 'Impressions' },
          { type: 'line', id: 'Page visits' },
        ]}
        modalZIndex={new FixedZIndex(11)}
        onVisualPatternChange={() =>
          setVisualPatternSelected((value) =>
            value === 'default' ? 'visualPattern' : 'default'
          )
        }
        range={{
          xAxisBottom: ['auto', 'auto'],
        }}
        tickFormatter={{
          yAxisLeft: (value) => {
            if (value >= 1000000) return `${value / 1000000}m`;
            if (value >= 1000) return `${value / 1000}k`;
            return value;
          },
          timeseries: (date) =>
            `${new Intl.DateTimeFormat('en-US', { month: 'short' }).format(
              date
            )}-${new Intl.DateTimeFormat('en-US', { day: '2-digit' }).format(
              date
            )}`,
        }}
        title="Performance over time"
        type="line"
        visualPatternSelected={visualPatternSelected}
      />
    </Flex>
  );
}

Combo

This combines a bar graph with a line graph. It is useful to see both categories and a trend or range over time.

Props: type="combo" elements=[{..., type:'bar' or type:'line'}]

import { useState } from 'react';
import { FixedZIndex, Flex } from 'gestalt';
import { ChartGraph } from 'gestalt-charts';

export default function Example() {
  const [visualPatternSelected, setVisualPatternSelected] = useState('default');

  const data = [
    {
      name: 'Quarter 1',
      'This year': 850000,
      Profit: 870000,
      'Last year': 980000,
    },
    {
      name: 'Quarter 2',
      'This year': 800000,
      Profit: 690000,
      'Last year': 590000,
    },
    {
      name: 'Quarter 3',
      'This year': 890000,
      Profit: 850000,
      'Last year': 950000,
    },
    {
      name: 'Quarter 4',
      'This year': 870000,
      Profit: 550000,
      'Last year': 830000,
    },
  ];

  return (
    <Flex direction="column" gap={2} height="100%" width="100%">
      <ChartGraph
        accessibilityLabel="Example of combo chart"
        data={data}
        elements={[
          { type: 'bar', id: 'This year' },
          { type: 'bar', id: 'Profit' },
          { type: 'line', id: 'Last year' },
        ]}
        modalZIndex={new FixedZIndex(11)}
        onVisualPatternChange={() =>
          setVisualPatternSelected((value) =>
            value === 'default' ? 'visualPattern' : 'default'
          )
        }
        tickFormatter={{
          yAxisLeft: (value) => {
            if (value >= 1000000) return `${value / 1000000}m`;
            if (value >= 1000) return `${value / 1000}k`;
            return value;
          },
        }}
        title="Product group sales:"
        type="combo"
        visualPatternSelected={visualPatternSelected}
      />
    </Flex>
  );
}

Biaxial

Biaxial graphs show two y-axis. They're used when either comparing two categories with mixed types of data—for example, clicks vs spend—or when there is a significant difference between values. A suggestion is to move to a biaxial chart if there is a 50%+ difference between compared values.

Props: layout="verticalBiaxial" layout="horizontalBiaxial"

import { useState } from 'react';
import { FixedZIndex, Flex } from 'gestalt';
import { ChartGraph } from 'gestalt-charts';

export default function Example() {
  const [visualPatternSelected, setVisualPatternSelected] = useState('default');

  const data = [
    {
      name: new Date(2023, 0, 1).getTime(),
      Spend: 40000,
      'Total ROAS (Checkout)': 570000,
    },
    {
      name: new Date(2023, 1, 1).getTime(),
      Spend: 45000,
      'Total ROAS (Checkout)': 690000,
    },
    {
      name: new Date(2023, 2, 1).getTime(),
      Spend: 55000,
      'Total ROAS (Checkout)': 850000,
    },
    {
      name: new Date(2023, 3, 1).getTime(),
      Spend: 70000,
      'Total ROAS (Checkout)': 550000,
    },
    {
      name: new Date(2023, 4, 1).getTime(),
      Spend: 830000,
      'Total ROAS (Checkout)': 1000000,
    },
  ];

  return (
    <Flex direction="column" gap={2} height="100%" width="100%">
      <ChartGraph
        accessibilityLabel="Example of line chart"
        data={data}
        elements={[
          { type: 'line', id: 'Spend', axis: 'left' },
          { type: 'line', id: 'Total ROAS (Checkout)', axis: 'right' },
        ]}
        layout="verticalBiaxial"
        modalZIndex={new FixedZIndex(11)}
        onVisualPatternChange={() =>
          setVisualPatternSelected((value) =>
            value === 'default' ? 'visualPattern' : 'default'
          )
        }
        range={{ xAxisBottom: ['auto', 'auto'] }}
        tickFormatter={{
          yAxisLeft: (value) => {
            if (value >= 500) return `$${value / 500}k`;
            return value;
          },
          yAxisRight: (value) => {
            if (value >= 1000) return `${value / 1000}k`;
            return value;
          },
          timeseries: (date) =>
            `${new Intl.DateTimeFormat('en-US', { month: 'short' }).format(
              date
            )} ${new Intl.DateTimeFormat('en-US', { day: '2-digit' }).format(
              date
            )}`,
        }}
        title="Performance over last 30 days"
        type="line"
        visualPatternSelected={visualPatternSelected}
      />
    </Flex>
  );
}

ChartGraph's header support a title, description, a HelpButton, and the accessibility features: tabular data and visual patterns switch buttons.

  1. Title. A title for the graph in case it’s not displayed elsewhere on the screen.
  2. Description. An optional description is available if more context is needed.
  3. HelpButton
  4. Tabular data switch button. Not available yet.
  5. Visual pattern switch button.

Props: title description helpButton

import { Flex, HelpButton } from 'gestalt';
import { ChartGraph } from 'gestalt-charts';

export default function Example() {
  const data = [
    {
      name: 'Quarter 1',
      Clicks: 850000,
      Conversions: 870000,
    },
    {
      name: 'Quarter 2',
      Clicks: 800000,
      Conversions: 690000,
    },
    {
      name: 'Quarter 3',
      Clicks: 890000,
      Conversions: 850000,
    },
    {
      name: 'Quarter 4',
      Clicks: 870000,
      Conversions: 550000,
    },
  ];
  return (
    <Flex direction="column" gap={2} height="100%" width="100%">
      <ChartGraph
        accessibilityLabel="Example of chart with title and description"
        data={data}
        description="Includes both web and mobile"
        elements={[
          {
            type: 'bar',
            id: 'Clicks',
            axis: 'left',
          },
          {
            type: 'line',
            id: 'Conversions',
            axis: 'right',
          },
        ]}
        helpButton={
          <HelpButton
            accessibilityLabel="Click to learn more this ChartGraph"
            accessibilityPopoverLabel="Expanded information about this ChartGraph"
            link={{
              href: 'https://help.pinterest.com/en/business/article/conversion-insights/',
              text: 'Read our documentation',
              accessibilityLabel: 'Visit our Help center',
            }}
            text="If you want to learn more about Clicks vs conversions, visit our Help center."
          />
        }
        layout="verticalBiaxial"
        onVisualPatternChange={() => {}}
        title="Clicks vs conversions"
        visualPatternSelected="disabled"
      />
    </Flex>
  );
}

Tooltip

For showing more precise details on hover.

Props: renderTooltip

Default tooltip
import { useState } from 'react';
import { Flex } from 'gestalt';
import { ChartGraph } from 'gestalt-charts';

export default function Example() {
  const [visualPatternSelected, setVisualPatternSelected] = useState('default');

  const data = [
    {
      name: 'iOS',
      '18-30': 850000,
      '30-50': 870000,
      '50+': 980000,
    },
    {
      name: 'Android',
      '18-30': 800000,
      '30-50': 690000,
      '50+': 590000,
    },
    {
      name: 'Web',
      '18-30': 890000,
      '30-50': 850000,
      '50+': 950000,
    },
  ];

  return (
    <Flex direction="column" gap={2} height="100%" width="100%">
      <ChartGraph
        accessibilityLabel="Example of chart with tooltip"
        data={data}
        elements={[
          { type: 'bar', id: '18-30' },
          { type: 'bar', id: '30-50' },
          { type: 'bar', id: '50+' },
        ]}
        initialTicks={3}
        onVisualPatternChange={() =>
          setVisualPatternSelected((value) =>
            value === 'default' ? 'visualPattern' : 'default'
          )
        }
        tickFormatter={{
          yAxisLeft: (value) => {
            if (value >= 1000000) return `${value / 1000000}m`;
            if (value >= 1000) return `${value / 1000}k`;
            return value;
          },
        }}
        title="Views by demographics and device"
        type="bar"
        visualPatternSelected={visualPatternSelected}
      />
    </Flex>
  );
}

Custom tooltip
import { useState } from 'react';
import { Flex, Text } from 'gestalt';
import { ChartGraph } from 'gestalt-charts';

export default function Example() {
  const [visualPatternSelected, setVisualPatternSelected] = useState('default');

  const data = [
    {
      name: 'USA',
      '20-30': 100,
      '40-50': 200,
      '50-60': 300,
    },
    {
      name: 'Europe',
      '20-30': 200,
      '40-50': 300,
      '50-60': 400,
    },
  ];

  return (
    <Flex direction="column" height="100%" width="100%">
      <ChartGraph
        accessibilityLabel="Example of chart with tooltip"
        data={data}
        elements={[
          {
            type: 'bar',
            id: '20-30',
          },
          {
            type: 'bar',
            id: '40-50',
          },
          {
            type: 'bar',
            id: '50-60',
          },
        ]}
        initialTicks={3}
        onVisualPatternChange={() =>
          setVisualPatternSelected((value) =>
            value === 'default' ? 'visualPattern' : 'default'
          )
        }
        renderTooltip={({ label, payload, active }) =>
          active && Array.isArray(payload) ? (
            <Flex direction="column" gap={2}>
              <Flex.Item>
                {payload.map((payloadData) => (
                  <Flex key={payloadData.name} alignItems="center" gap={2}>
                    <ChartGraph.LegendIcon payloadData={payloadData} />
                    <Flex.Item flex="grow">
                      <Text size="100">{payloadData.name}</Text>
                    </Flex.Item>
                    <Text size="200" weight="bold">
                      ${payloadData.value}
                    </Text>
                  </Flex>
                ))}
              </Flex.Item>
              <Text color="subtle" size="100">
                {label}
              </Text>
            </Flex>
          ) : null
        }
        tickFormatter={{ yAxisLeft: (value) => `${value}m` }}
        title="MAU per regions"
        type="bar"
        visualPatternSelected={visualPatternSelected}
      />
    </Flex>
  );
}

Legend

Graphs that show more than one category should include a legend to clarify what color or pattern belongs to which category. For single axis charts, a legend isn’t needed when TagData or TileData are being used. Legend positions automatically on each layout for easier comprehension.

Props: legend

import { useState } from 'react';
import { FixedZIndex, Flex } from 'gestalt';
import { ChartGraph } from 'gestalt-charts';

export default function Example() {
  const [visualPatternSelected, setVisualPatternSelected] = useState('default');

  const data = [
    {
      name: new Date(2023, 0, 1).getTime(),
      California: 1500000,
      Arizona: 500000,
    },
    {
      name: new Date(2023, 1, 2).getTime(),
      California: 1000000,
      Arizona: 400000,
    },
    {
      name: new Date(2023, 2, 3).getTime(),
      California: 1500000,
      Arizona: 500000,
    },
    {
      name: new Date(2023, 3, 4).getTime(),
      California: 1000000,
      Arizona: 400000,
    },
    {
      name: new Date(2023, 4, 5).getTime(),
      California: 1500000,
      Arizona: 500000,
    },
    {
      name: new Date(2023, 5, 6).getTime(),
      California: 1000000,
      Arizona: 400000,
    },
  ];

  return (
    <Flex direction="column" gap={2} height="100%" width="100%">
      <ChartGraph
        accessibilityLabel="Example of chart with tooltip"
        data={data}
        elements={[
          { type: 'bar', id: 'California' },
          { type: 'bar', id: 'Arizona' },
        ]}
        modalZIndex={new FixedZIndex(11)}
        onVisualPatternChange={() =>
          setVisualPatternSelected((value) =>
            value === 'default' ? 'visualPattern' : 'default'
          )
        }
        tickFormatter={{
          yAxisLeft: (value) => {
            if (value >= 1000000) return `${value / 1000000}m`;
            if (value >= 1000) return `${value / 1000}k`;
            return value;
          },
          timeseries: (date) =>
            `${new Intl.DateTimeFormat('en-US', { month: 'short' }).format(
              date
            )} ${new Intl.DateTimeFormat('en-US', { day: '2-digit' }).format(
              date
            )}`,
        }}
        title="Average spend by region"
        type="bar"
        visualPatternSelected={visualPatternSelected}
      />
    </Flex>
  );
}

ReferenceArea

Use to highlight an area in a graph for extra context. A common example is showing when data isn’t available.

Props: referenceAreas

import { Flex } from 'gestalt';
import { ChartGraph } from 'gestalt-charts';

export default function Example() {
  const data = [
    {
      name: new Date(2023, 0, 1).getTime(),
      Impressions: 850000,
    },
    {
      name: new Date(2023, 0, 2).getTime(),
      Impressions: 800000,
    },
    {
      name: new Date(2023, 0, 3).getTime(),
      Impressions: 890000,
    },
    {
      name: new Date(2023, 0, 4).getTime(),
      Impressions: 870000,
    },
    {
      name: new Date(2023, 0, 5).getTime(),
      Impressions: 830000,
    },
    {
      name: new Date(2023, 0, 6).getTime(),
      Impressions: 930000,
    },
    {
      name: new Date(2023, 0, 7).getTime(),
      Impressions: 630000,
    },
    {
      name: new Date(2023, 0, 8).getTime(),
      Impressions: 730000,
    },
    {
      name: new Date(2023, 0, 9).getTime(),
      Impressions: 890000,
    },
  ];

  return (
    <Flex direction="column" gap={2} height="100%" width="100%">
      <ChartGraph
        accessibilityLabel="Example of chart with reference area"
        data={data}
        elements={[{ type: 'line', id: 'Impressions' }]}
        onVisualPatternChange={() => {}}
        range={{
          yAxisLeft: [0, 1000000],
          xAxisBottom: ['auto', new Date(2023, 0, 10).getTime()],
        }}
        referenceAreas={[
          {
            id: 'ExampleBD',
            label: 'Real-time data not available',
            x1: new Date(2023, 0, 9).getTime(),
            x2: new Date(2023, 0, 10).getTime(),
            yAxisId: 'left',
            y1: 0,
            y2: 1000000,
          },
        ]}
        tickFormatter={{
          yAxisLeft: (value) => {
            if (value >= 1000000) return `${value / 1000000}m`;
            if (value >= 1000) return `${value / 1000}k`;
            return value;
          },
          timeseries: (date) =>
            `${new Intl.DateTimeFormat('en-US', { month: 'short' }).format(
              date
            )}-${new Intl.DateTimeFormat('en-US', { day: '2-digit' }).format(
              date
            )}`,
        }}
        title="Impressions over time"
        type="line"
        visualPatternSelected="disabled"
      />
    </Flex>
  );
}

Precision in line graphs

To show exact and accurate data, lines should be rectilinear. When showing trends, forecasts and imprecise data, then lines should be curved to denote that these are just approximations.

Props: elements=[{..., type:'line', precision='estimate'}]

import { FixedZIndex, Flex } from 'gestalt';
import { ChartGraph } from 'gestalt-charts';

export default function Example() {
  const data = [
    { name: new Date(2023, 0, 1).getTime(), 'Actual data': 1000 },
    { name: new Date(2023, 1, 1).getTime(), 'Actual data': 1567 },
    { name: new Date(2023, 2, 1).getTime(), 'Actual data': 1005 },
    { name: new Date(2023, 3, 1).getTime(), 'Actual data': 1003 },
    { name: new Date(2023, 4, 1).getTime(), 'Actual data': 1100 },
    { name: new Date(2023, 5, 1).getTime(), 'Actual data': 1030 },
    { name: new Date(2023, 6, 1).getTime(), 'Actual data': 1089 },
    {
      name: new Date(2023, 7, 1).getTime(),
      'Actual data': 1065,
      Forecast: 1065,
    },
    { name: new Date(2023, 8, 1).getTime(), Forecast: 1089 },
    { name: new Date(2023, 9, 1).getTime(), Forecast: 1030 },
    { name: new Date(2023, 10, 1).getTime(), Forecast: 1990 },
    { name: new Date(2023, 11, 1).getTime(), Forecast: 2034 },
  ];

  return (
    <Flex direction="column" gap={2} height="100%" width="100%">
      <ChartGraph
        accessibilityLabel="Example of time series chart"
        data={data}
        elements={[
          { type: 'line', id: 'Actual data', precision: 'exact' },
          { type: 'line', id: 'Forecast', precision: 'estimate' },
        ]}
        modalZIndex={new FixedZIndex(11)}
        onVisualPatternChange={() => {}}
        range={{
          xAxisBottom: ['auto', 'auto'],
        }}
        tickFormatter={{
          timeseries: (date) =>
            `${new Intl.DateTimeFormat('en-US', { month: 'short' }).format(
              date
            )}-${new Intl.DateTimeFormat('en-US', { day: '2-digit' }).format(
              date
            )}`,
        }}
        title="Forecast for 2023"
        type="line"
        visualPatternSelected="disabled"
      />
    </Flex>
  );
}

Colors

Colors on data series are set automatically for best contrast.

If different graphs need to be compared simultaneously, see example below, color in time series can be set in the elements prop setting each color color='01' individually.

color="01"
color="02"
color="03"
color="04"
color="05"
color="06"
color="07"
color="08"
color="09"
color="10"
color="11"
color="12"
import { useState } from 'react';
import { Flex } from 'gestalt';
import { ChartGraph } from 'gestalt-charts';

export default function Example() {
  const [visualPatternSelected, setVisualPatternSelected] = useState('default');

  const data = [
    {
      name: 'A',
      Series_01: 100,
    },
    {
      name: 'B',
      Series_01: 200,
    },
    {
      name: 'C',
      Series_01: 300,
    },
  ];

  return (
    <Flex direction="column" width="100%" wrap>
      <Flex>
        <ChartGraph
          accessibilityLabel="Example of line chart color 01"
          data={data}
          description="Color 01"
          elements={[{ type: 'line', id: 'Series_01' }]}
          initialTicks={3}
          legend="none"
          onVisualPatternChange={() =>
            setVisualPatternSelected((value) =>
              value === 'default' ? 'visualPattern' : 'default'
            )
          }
          renderTooltip="none"
          title="ChartGraph A"
          type="line"
          visualPatternSelected={visualPatternSelected}
        />
        <ChartGraph
          accessibilityLabel="Example of line chart color 02"
          data={data}
          description="Color 02"
          elements={[{ type: 'line', id: 'Series_01', color: '02' }]}
          initialTicks={3}
          legend="none"
          onVisualPatternChange={() =>
            setVisualPatternSelected((value) =>
              value === 'default' ? 'visualPattern' : 'default'
            )
          }
          renderTooltip="none"
          title="ChartGraph B"
          type="line"
          visualPatternSelected={visualPatternSelected}
        />
      </Flex>
      <Flex>
        <ChartGraph
          accessibilityLabel="Example of line chart color 03"
          data={data}
          description="Color 03"
          elements={[{ type: 'line', id: 'Series_01', color: '03' }]}
          initialTicks={3}
          legend="none"
          onVisualPatternChange={() =>
            setVisualPatternSelected((value) =>
              value === 'default' ? 'visualPattern' : 'default'
            )
          }
          renderTooltip="none"
          title="ChartGraph C"
          type="line"
          visualPatternSelected={visualPatternSelected}
        />
        <ChartGraph
          accessibilityLabel="Example of line chart color 04"
          data={data}
          description="Color 04"
          elements={[{ type: 'line', id: 'Series_01', color: '04' }]}
          initialTicks={3}
          legend="none"
          onVisualPatternChange={() =>
            setVisualPatternSelected((value) =>
              value === 'default' ? 'visualPattern' : 'default'
            )
          }
          renderTooltip="none"
          title="ChartGraph D"
          type="line"
          visualPatternSelected={visualPatternSelected}
        />
      </Flex>
    </Flex>
  );
}

Layout

ChartGraph supports 4 layouts. Legend positions automatically on each layout for easier comprehension.

Single axis:

  1. vertical (default)
  2. horizontal

Dual axis:

  1. verticalBiaxial
  2. horizontalBiaxial

Props: layout

import { useState } from 'react';
import { Flex, RadioGroup } from 'gestalt';
import { ChartGraph } from 'gestalt-charts';

export default function Example() {
  const [visualPatternSelected, setVisualPatternSelected] = useState('default');
  const [type, setType] = useState('bar');
  const [layout, setLayout] = useState('vertical');

  let axisSeries01;
  let axisSeries02;

  if (layout === 'horizontalBiaxial') {
    axisSeries01 = 'bottom';
    axisSeries02 = 'top';
  }

  if (layout === 'verticalBiaxial') {
    axisSeries01 = 'left';
    axisSeries02 = 'right';
  }

  const data = [
    {
      name: 'A',
      Series_01: 100,
      Series_02: 200,
    },
    {
      name: 'B',
      Series_01: 200,
      Series_02: 300,
    },
  ];

  return (
    <Flex direction="column" gap={2} height="100%" width="100%">
      <Flex justifyContent="between" width="100%" wrap>
        <RadioGroup direction="row" id="layout-type" legend="ChartGraph type">
          <RadioGroup.RadioButton
            checked={type === 'bar'}
            id="layout-type-bar"
            label="Bar"
            name="bar"
            onChange={() => setType('bar')}
            size="sm"
            value="bar"
          />
          <RadioGroup.RadioButton
            checked={type === 'line'}
            id="layout-type-line"
            label="Line"
            name="line"
            onChange={() => setType('line')}
            size="sm"
            value="line"
          />
          <RadioGroup.RadioButton
            checked={type === 'combo'}
            id="layout-type-combo"
            label="Combo"
            name="combo"
            onChange={() => setType('combo')}
            size="sm"
            value="combo"
          />
        </RadioGroup>
        <RadioGroup direction="row" id="layout_layout" legend="Layout">
          <RadioGroup.RadioButton
            checked={layout === 'horizontal'}
            id="layout_layout-horizontal"
            label="Horizontal"
            name="horizontal"
            onChange={() => setLayout('horizontal')}
            size="sm"
            value="horizontal"
          />
          <RadioGroup.RadioButton
            checked={layout === 'horizontalBiaxial'}
            id="layout_layout-horizontalBiaxial"
            label="HorizontalBiaxial"
            name="horizontalBiaxial"
            onChange={() => setLayout('horizontalBiaxial')}
            size="sm"
            value="horizontalBiaxial"
          />
          <RadioGroup.RadioButton
            checked={layout === 'vertical'}
            id="layout_layout-vertical"
            label="Vertical"
            name="vertical"
            onChange={() => setLayout('vertical')}
            size="sm"
            value="vertical"
          />
          <RadioGroup.RadioButton
            checked={layout === 'verticalBiaxial'}
            id="layout_layout-verticalBiaxial"
            label="VerticalBiaxial"
            name="verticalBiaxial"
            onChange={() => setLayout('verticalBiaxial')}
            size="sm"
            value="verticalBiaxial"
          />
        </RadioGroup>
      </Flex>

      <ChartGraph
        accessibilityLabel="Example of chart with decal custom dimension"
        data={data}
        elements={[
          {
            type: type === 'combo' ? 'bar' : type,
            id: 'Series_01',
            axis: axisSeries01,
          },
          {
            type: type === 'combo' ? 'line' : type,
            id: 'Series_02',
            axis: axisSeries02,
          },
        ]}
        layout={layout}
        legend="none"
        onVisualPatternChange={() =>
          setVisualPatternSelected((value) =>
            value === 'default' ? 'visualPattern' : 'default'
          )
        }
        title="ChartGraph"
        titleDisplay="hidden"
        type={type}
        visualPatternSelected={visualPatternSelected}
      />
    </Flex>
  );
}

Range

ChartGraph automatically sets the minimum and maximum axis values. The range prop allows adjusting them in case we need broader range values in the axis.

Props: range

import { useState } from 'react';
import { Flex, RadioGroup } from 'gestalt';
import { ChartGraph } from 'gestalt-charts';

export default function Example() {
  const [visualPatternSelected, setVisualPatternSelected] = useState('default');
  const [type, setType] = useState('bar');

  const data = [
    {
      name: 'A',
      Percentage: 20,
      Absolute: 2000,
    },
    {
      name: 'B',
      Percentage: 40,
      Absolute: 3000,
    },
    {
      name: 'C',
      Percentage: 80,
      Absolute: 4000,
    },
  ];

  return (
    <Flex direction="column" gap={2} height="100%" width="100%">
      <RadioGroup direction="row" id="range" legend="ChartGraph type">
        <RadioGroup.RadioButton
          checked={type === 'bar'}
          id="range-bar"
          label="Bar"
          name="bar"
          onChange={() => setType('bar')}
          size="sm"
          value="bar"
        />
        <RadioGroup.RadioButton
          checked={type === 'line'}
          id="range-line"
          label="Line"
          name="line"
          onChange={() => setType('line')}
          size="sm"
          value="line"
        />
        <RadioGroup.RadioButton
          checked={type === 'combo'}
          id="range-combo"
          label="Combo"
          name="combo"
          onChange={() => setType('combo')}
          size="sm"
          value="combo"
        />
      </RadioGroup>
      <ChartGraph
        accessibilityLabel="Example of range in charts"
        data={data}
        elements={[
          {
            type: type === 'combo' ? 'bar' : type,
            id: 'Percentage',
            axis: 'left',
          },
          {
            type: type === 'combo' ? 'line' : type,
            id: 'Absolute',
            axis: 'right',
          },
        ]}
        layout="verticalBiaxial"
        onVisualPatternChange={() =>
          setVisualPatternSelected((value) =>
            value === 'default' ? 'visualPattern' : 'default'
          )
        }
        range={{ yAxisLeft: [0, 100] }}
        title="ChartGraph"
        titleDisplay="hidden"
        type={type}
        visualPatternSelected={visualPatternSelected}
      />
    </Flex>
  );
}

Responsive

ChartGraph is responsive. ChartGraph's width adjusts to the parent container. In order to render properly, ChartGraph requires a parent container with set dimensions.

For vertical layouts, ChartGraph has a set height based on the amount of ticks (five or three). For horizontal layouts, ChartGraph has a set height.

When ChartGraphs are contained within small containers (under 576px wide), set initialTicks={3} to prevent ChartGraph to flick.

import { useId, useRef, useState } from 'react';
import { Box, Flex, Label, RadioGroup, Text } from 'gestalt';
import { ChartGraph } from 'gestalt-charts';

export default function Example() {
  const labelId = useId();
  const [width, setWidth] = useState(700);
  const scrollContainerRef = useRef();
  const updateWidth = ({ target }) => {
    setWidth(Number(target.value));
  };

  const [visualPatternSelected, setVisualPatternSelected] = useState('default');
  const [type, setType] = useState('bar');
  const [layout, setLayout] = useState('horizontal');

  let axisSeries01;
  let axisSeries02;

  if (layout === 'horizontalBiaxial') {
    axisSeries01 = 'bottom';
    axisSeries02 = 'top';
  }

  if (layout === 'verticalBiaxial') {
    axisSeries01 = 'left';
    axisSeries02 = 'right';
  }

  const data = [
    {
      name: 'A',
      Series_01: 100,
      Series_02: 200,
    },
    {
      name: 'B',
      Series_01: 200,
      Series_02: 300,
    },
  ];

  return (
    <Box padding={2} width="100%">
      <Flex direction="column" gap={5} width="100%">
        <Flex justifyContent="between" width="100%">
          <RadioGroup
            direction="row"
            id="responsive-type"
            legend="ChartGraph type"
          >
            <RadioGroup.RadioButton
              checked={type === 'bar'}
              id="responsive-type-bar"
              label="Bar"
              name="bar"
              onChange={() => setType('bar')}
              size="sm"
              value="bar"
            />
            <RadioGroup.RadioButton
              checked={type === 'line'}
              id="responsive-type-line"
              label="Line"
              name="line"
              onChange={() => setType('line')}
              size="sm"
              value="line"
            />
            <RadioGroup.RadioButton
              checked={type === 'combo'}
              id="responsive-type-combo"
              label="Combo"
              name="combo"
              onChange={() => setType('combo')}
              size="sm"
              value="combo"
            />
          </RadioGroup>
          <RadioGroup direction="row" id="responsive_layout" legend="Layout">
            <RadioGroup.RadioButton
              checked={layout === 'horizontal'}
              id="responsive_layout-horizontal"
              label="Horizontal"
              name="horizontal"
              onChange={() => setLayout('horizontal')}
              size="sm"
              value="horizontal"
            />
            <RadioGroup.RadioButton
              checked={layout === 'horizontalBiaxial'}
              id="responsive_layout-horizontalBiaxial"
              label="HorizontalBiaxial"
              name="horizontalBiaxial"
              onChange={() => setLayout('horizontalBiaxial')}
              size="sm"
              value="horizontalBiaxial"
            />
            <RadioGroup.RadioButton
              checked={layout === 'vertical'}
              id="responsive_layout-vertical"
              label="Vertical"
              name="vertical"
              onChange={() => setLayout('vertical')}
              size="sm"
              value="vertical"
            />
            <RadioGroup.RadioButton
              checked={layout === 'verticalBiaxial'}
              id="responsive_layout-verticalBiaxial"
              label="VerticalBiaxial"
              name="verticalBiaxial"
              onChange={() => setLayout('verticalBiaxial')}
              size="sm"
              value="verticalBiaxial"
            />
          </RadioGroup>
        </Flex>
        <Flex alignItems="center" direction="column">
          <Flex>
            <Label htmlFor={labelId}>
              <Text>Container Width</Text>
            </Label>
            <Text>{`: ${width}px`}</Text>
          </Flex>
          <input
            defaultValue={800}
            id={labelId}
            max={800}
            min={200}
            onChange={updateWidth}
            step={1}
            style={{ width: '400px', display: 'block', margin: '10px auto' }}
            type="range"
          />
        </Flex>

        <div
          ref={(el) => {
            scrollContainerRef.current = el;
          }}
          style={{
            height: '300px',
            margin: '0 auto',
            outline: '3px solid #ddd',
            overflowY: 'scroll',
            width: `${width}px`,
          }}
        >
          <ChartGraph
            accessibilityLabel="Example of chart with decal custom dimension"
            data={data}
            elements={[
              {
                type: type === 'combo' ? 'bar' : type,
                id: 'Series_01',
                axis: axisSeries01,
              },
              {
                type: type === 'combo' ? 'line' : type,
                id: 'Series_02',
                axis: axisSeries02,
              },
            ]}
            layout={layout}
            legend="none"
            onVisualPatternChange={() =>
              setVisualPatternSelected((value) =>
                value === 'default' ? 'visualPattern' : 'default'
              )
            }
            title="ChartGraph"
            titleDisplay="hidden"
            type={type}
            visualPatternSelected={visualPatternSelected}
          />
        </div>
      </Flex>
    </Box>
  );
}

Selectors

ChartGraph supports TileData and TagData. The selection of TileData and TagData controls the data series displayed on ChartGraph.

import { useState } from 'react';
import { TileData } from 'gestalt';
import { ChartGraph } from 'gestalt-charts';

export default function Example() {
  const dataA = [
    {
      name: 'A',
      Impressions: 100,
    },
    {
      name: 'B',
      Impressions: 200,
    },
    {
      name: 'C',
      Impressions: 300,
    },
  ];
  const dataB = [
    {
      name: 'A',
      Engagement: 90,
    },
    {
      name: 'B',
      Engagement: 180,
    },
    {
      name: 'C',
      Engagement: 250,
    },
  ];

  const [selectedId, setSelectedId] = useState('01');
  const isSelected = (id) => selectedId === id;

  const getColor = (value) => {
    const colorMap = {
      '01': '01',
      '02': '02',
      '03': '03',
      '04': '04',
      '05': '05',
      '06': '06',
      '07': '07',
      '08': '08',
      '09': '09',
      10: '10',
      11: '11',
      12: '12',
    };

    return colorMap[value];
  };

  return (
    <ChartGraph
      accessibilityLabel="Example of Bar chart"
      data={selectedId === '01' ? dataA : dataB}
      description="Description"
      elements={[
        {
          type: 'bar',
          id: selectedId === '01' ? 'Impressions' : 'Engagement',
          color: getColor(selectedId || '01'),
        },
      ]}
      initialTicks={3}
      legend="none"
      onVisualPatternChange={() => {}}
      title="Title"
      type="bar"
      visualPatternSelected="disabled"
    >
      <TileData
        color="01"
        id="01"
        onTap={({ id }) => setSelectedId(id)}
        selected={isSelected('01')}
        title="Impressions"
        trend={{ value: 29, accessibilityLabel: 'Trending up' }}
        value="10M"
      />
      <TileData
        color="02"
        id="02"
        onTap={({ id }) => setSelectedId(id)}
        selected={isSelected('02')}
        title="Engagement"
        trend={{ value: 29, accessibilityLabel: 'Trending up' }}
        value="2M"
      />
    </ChartGraph>
  );
}

import { useEffect, useState } from 'react';
import { TagData } from 'gestalt';
import { ChartGraph } from 'gestalt-charts';

export default function Example() {
  const [visualPatternSelected, setVisualPatternSelected] = useState('default');

  const [elements, setElements] = useState([]);

  const [selectedId, setSelectedId] = useState(['Campaign Autumn']);
  const isSelected = (id) => selectedId.includes(id);

  const getColor = (value) => {
    const colorMap = {
      '01': '01',
      '02': '02',
      '03': '03',
      '04': '04',
      '05': '05',
      '06': '06',
      '07': '07',
      '08': '08',
      '09': '09',
      10: '10',
      11: '11',
      12: '12',
    };

    return colorMap[value];
  };

  const handleSelectors = ({ id, selected }) => {
    if (!selected) {
      setSelectedId((values) => values.filter((idValue) => idValue !== id));
    }
    if (selected) {
      setSelectedId((idValues) => [...idValues, id]);
    }
  };

  useEffect(() => {
    const elementsArray = [
      {
        type: 'bar',
        id: 'Campaign Autumn',
        color: getColor('01'),
      },
      {
        type: 'bar',
        id: 'Campaign Winter',
        color: getColor('02'),
      },
      {
        type: 'bar',
        id: 'Campaign Spring',
        color: getColor('03'),
      },
    ];

    const newElements = selectedId
      .map((idToMap) => elementsArray.filter(({ id }) => id === idToMap))
      .flat();

    setElements(newElements);
  }, [selectedId]);

  return (
    <ChartGraph
      accessibilityLabel="Example of Bar chart"
      data={[
        {
          name: 'NorthWest',
          'Campaign Autumn': 100,
          'Campaign Winter': 90,
          'Campaign Spring': 10,
        },
        {
          name: 'Sunbelt',
          'Campaign Autumn': 200,
          'Campaign Winter': 180,
          'Campaign Spring': 50,
        },
        {
          name: 'East Coast',
          'Campaign Autumn': 300,
          'Campaign Winter': 250,
          'Campaign Spring': 100,
        },
      ]}
      elements={elements}
      initialTicks={3}
      legend="none"
      onVisualPatternChange={() =>
        setVisualPatternSelected((value) =>
          value === 'default' ? 'visualPattern' : 'default'
        )
      }
      title="Clickthroughs per region"
      type="bar"
      visualPatternSelected={visualPatternSelected}
    >
      <TagData
        color="01"
        id="Campaign Autumn"
        onTap={({ id, selected }) => handleSelectors({ id, selected })}
        selected={isSelected('Campaign Autumn')}
        showCheckbox
        text="Campaign Autumn"
      />

      <TagData
        color="02"
        id="Campaign Winter"
        onTap={({ id, selected }) => handleSelectors({ id, selected })}
        selected={isSelected('Campaign Winter')}
        showCheckbox
        text="Campaign Winter"
      />
      <TagData
        color="03"
        id="Campaign Spring"
        onTap={({ id, selected }) => handleSelectors({ id, selected })}
        selected={isSelected('Campaign Spring')}
        showCheckbox
        text="Campaign Spring"
      />
    </ChartGraph>
  );
}

Tick format

ChartGraph allows to format the values in the axis' ticks. For example, to translate numeric values to shorter ones (1000 => 1k) or to format dates based on locale.

Props: tickFormatter.

When localizing dates, use tickFormatter.timestamps as it traslates the values in the tooltip as well. Avoid using comma separators for dates as .csv files use them for cell separation. tickFormatter.xAxisBottom overrides tickFormatter.timeseries when both are present, in case tooltip and x axis present different date formats.

import { ChartGraph } from 'gestalt-charts';

export default function Example() {
  const data = [
    {
      name: 'A',
      Series_01: 1000300,
    },
    {
      name: 'B',
      Series_01: 2000600,
    },
    {
      name: 'C',
      Series_01: 3001200,
    },
  ];

  return (
    <ChartGraph
      accessibilityLabel="Example of Bar chart"
      data={data}
      elements={[{ type: 'bar', id: 'Series_01' }]}
      legend="none"
      onVisualPatternChange={() => {}}
      tickFormatter={{ yAxisLeft: (value) => `${value / 1000000}M` }}
      title="ChartGraph"
      titleDisplay="hidden"
      type="bar"
      visualPatternSelected="disabled"
    />
  );
}

Time series

ChartGraph supports timeseries. To enable timeseries, set tickFormatter.timeseries. Time series charts are only supported in vertical layout.

Props: tickFormatter.timeseries.

import { useState } from 'react';
import { FixedZIndex, Flex, RadioGroup } from 'gestalt';
import { ChartGraph } from 'gestalt-charts';

export default function Example() {
  const [visualPatternSelected, setVisualPatternSelected] = useState('default');
  const [type, setType] = useState('line');

  const data = [
    { name: new Date(2023, 0, 1).getTime(), Series_01: 1000, Series_02: 100 },
    { name: new Date(2023, 0, 2).getTime(), Series_01: 1005, Series_02: 200 },
    { name: new Date(2023, 0, 3).getTime(), Series_01: 1003, Series_02: 150 },
    { name: new Date(2023, 0, 4).getTime(), Series_01: 1100, Series_02: 130 },
    { name: new Date(2023, 0, 5).getTime(), Series_01: 1030, Series_02: 147 },
    { name: new Date(2023, 0, 6).getTime(), Series_01: 1089, Series_02: 189 },
    { name: new Date(2023, 0, 7).getTime(), Series_01: 1065, Series_02: 118 },
    { name: new Date(2023, 0, 8).getTime(), Series_01: 1090, Series_02: 177 },
  ];

  return (
    <Flex direction="column" gap={2} height="100%" width="100%">
      <RadioGroup direction="row" id="timeseries" legend="ChartGraph type">
        <RadioGroup.RadioButton
          checked={type === 'bar'}
          id="timeseries-bar"
          label="Bar"
          name="bar"
          onChange={() => setType('bar')}
          size="sm"
          value="bar"
        />
        <RadioGroup.RadioButton
          checked={type === 'line'}
          id="timeseries-line"
          label="Line"
          name="line"
          onChange={() => setType('line')}
          size="sm"
          value="line"
        />
        <RadioGroup.RadioButton
          checked={type === 'combo'}
          id="timeseries-combo"
          label="Combo"
          name="combo"
          onChange={() => setType('combo')}
          size="sm"
          value="combo"
        />
      </RadioGroup>
      <ChartGraph
        accessibilityLabel="Example of time series chart"
        data={data}
        elements={[
          { type: type === 'combo' ? 'bar' : type, id: 'Series_01' },
          { type: type === 'combo' ? 'line' : type, id: 'Series_02' },
        ]}
        modalZIndex={new FixedZIndex(11)}
        onVisualPatternChange={() =>
          setVisualPatternSelected((value) =>
            value === 'default' ? 'visualPattern' : 'default'
          )
        }
        range={{
          xAxisBottom: ['auto', 'auto'],
        }}
        tickFormatter={{
          timeseries: (date) =>
            `${new Intl.DateTimeFormat('en-US', { month: 'short' }).format(
              date
            )}-${new Intl.DateTimeFormat('en-US', { day: '2-digit' }).format(
              date
            )}`,
        }}
        title="ChartGraph"
        titleDisplay="hidden"
        type={type}
        visualPatternSelected={visualPatternSelected}
      />
    </Flex>
  );
}

Writing

Do
  • Keep labels short so that they don’t wrap and make it hard to read data
  • Use abbreviations that are commonly understood and can be translated to all supported languages. For more on abbreviations, see our Content standards.
Don't
  • Create extra-long labels that have to wrap or truncate
  • Use abbreviations that are only understood internally or that don't translate well.

Component quality checklist

Component quality checklist
Quality item
Status
Status description
Figma Library
Ready
Component is available in Figma for web and mobile web.
Responsive Web
Ready
Component responds to changing viewport sizes in web and mobile web.

Data visualization guidelines
Principles, use cases and guidelines for charts, graphs and micro-visualizations

TagData
TagData enables people to select multiple categories to compare with each other in a graph or chart.

TileData
TileData enables users to select multiple categories to compare with each other in a graph or chart view, while still being able to see all of the data points.

Table
Tables show data that's more complex and granular.