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:

A11y:

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<{
  name: string | number,
  [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.Element<typeof TileData>
| React.Element<typeof TagData>
| $ReadOnlyArray<React.Element<typeof TileData>>
| $ReadOnlyArray<React.Element<typeof TagData>>
-

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
React.Element<typeof HelpButton>
-

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
{ [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" | ((number) => number),
    number | "auto" | "dataMin" | "dataMax" | ((number) => number),
  ]
| {
    xAxisBottom?: [
      number | "auto" | "dataMin" | "dataMax" | ((number) => number),
      number | "auto" | "dataMin" | "dataMax" | ((number) => number),
    ],
    xAxisTop?: [
      number | "auto" | "dataMin" | "dataMax" | ((number) => number),
      number | "auto" | "dataMin" | "dataMax" | ((number) => number),
    ],

    yAxisLeft?: [
      number | "auto" | "dataMin" | "dataMax" | ((number) => number),
      number | "auto" | "dataMin" | "dataMax" | ((number) => number),
    ],

    yAxisRight?: [
      number | "auto" | "dataMin" | "dataMax" | ((number) => number),
      number | "auto" | "dataMin" | "dataMax" | ((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"
| (({
    active: ?boolean,
    payload: ?{ ... },
    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?: (number) => string | number,
  xAxisTop?: (number, number) => string | number,
  xAxisBottom?: (number, number) => string | number,
  yAxisRight?: (number, number) => string | number,
  yAxisLeft?: (number, 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
      title="ChartGraph"
      titleDisplay="hidden"
      accessibilityLabel="Example of chart with decal pattern in bars"
      visualPatternSelected={visualPatternSelected}
      onVisualPatternChange={() =>
        setVisualPatternSelected((value) =>
          value === 'default' ? 'visualPattern' : 'default'
        )
      }
      range={[0, 100]}
      type="bar"
      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' },
      ]}
    />
  );
}

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
      title="ChartGraph"
      titleDisplay="hidden"
      accessibilityLabel="Example of chart with decal pattern in lines"
      visualPatternSelected={visualPatternSelected}
      onVisualPatternChange={() =>
        setVisualPatternSelected((value) =>
          value === 'default' ? 'visualPattern' : 'default'
        )
      }
      type="line"
      tickFormatter={{
        timeseries: (date) =>
          `${new Intl.DateTimeFormat('en-US', { month: 'short' }).format(
            date
          )}-${new Intl.DateTimeFormat('en-US', { day: '2-digit' }).format(
            date
          )}`,
      }}
      range={{
        xAxisBottom: [
          new Date(2023, 0, 1).getTime(),
          new Date(2023, 11, 1).getTime(),
        ],
      }}
      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)}
    />
  );
}

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"
      visualPatternSelected={visualPatternSelected}
      onVisualPatternChange={() =>
        setVisualPatternSelected((value) =>
          value === 'default' ? 'visualPattern' : 'default'
        )
      }
      type="bar"
      title="ChartGraph"
      titleDisplay="hidden"
      data={data}
      elements={[
        { type: 'bar', id: 'Series_01' },
        { type: 'bar', id: 'Series_02' },
      ]}
    />
  );
}

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
      // $FlowExpectedError[incompatible-type] For demostration purposes
      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
        title="Eindrücke"
        description="Leistung im Laufe der Zeit. Impressionen geben an, wie oft Ihr Pin auf dem Bildschirm angezeigt wurde."
        labelMap={{
          Women: 'Frauen',
          Men: 'Männer',
          Users: 'Benutzer',
          Clickthroughs: 'Durchklicken',
        }}
        visualPatternSelected={visualPatternSelected}
        onVisualPatternChange={() =>
          setVisualPatternSelected((value) =>
            value === 'default' ? 'visualPattern' : 'default'
          )
        }
        accessibilityLabel="Beispiel für ein Liniendiagramm"
        type="bar"
        data={[
          {
            name: 'Women',
            Users: 100,
            Clickthroughs: 200,
          },
          {
            name: 'Men',
            Users: 200,
            Clickthroughs: 300,
          },
        ]}
        elements={[
          { type: 'bar', id: 'Users' },
          { type: 'bar', id: 'Clickthroughs' },
        ]}
      />
    </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,
    value: number,
    strokeDasharray: ?string | number,
    strokeWidth?: number,
    color: ?string,
    fill: ?string,
    legendType?: "line" | "rect",
    isLegend?: boolean,
  }
| { referenceArea: "default", isLegend?: boolean }
-

Data received from the renderTooltip.

See the custom tooltip variant 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 height="100%" width="100%" direction="column" gap={2}>
      <ChartGraph
        title="Regions in the US"
        accessibilityLabel="Example of a vertical bar chart"
        visualPatternSelected={visualPatternSelected}
        onVisualPatternChange={() =>
          setVisualPatternSelected((value) =>
            value === 'default' ? 'visualPattern' : 'default'
          )
        }
        type="bar"
        layout="horizontal"
        data={data}
        elements={[
          { type: 'bar', id: 'Impressions' },
          { type: 'bar', id: 'CPM' },
          { type: 'bar', id: 'CPC' },
        ]}
      />
    </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 height="100%" width="100%" direction="column" gap={2}>
      <ChartGraph
        title="Age"
        accessibilityLabel="Example of a vertical bar chart"
        visualPatternSelected={visualPatternSelected}
        onVisualPatternChange={() =>
          setVisualPatternSelected((value) =>
            value === 'default' ? 'visualPattern' : 'default'
          )
        }
        type="bar"
        data={data}
        elements={[
          { type: 'bar', id: 'Impressions' },
          { type: 'bar', id: 'CPM' },
          { type: 'bar', id: 'CPC' },
        ]}
      />
    </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 height="100%" width="100%" direction="column" gap={2}>
      <ChartGraph
        title="508 campaigns"
        stacked
        accessibilityLabel="Example of stacked bars chart"
        visualPatternSelected={visualPatternSelected}
        onVisualPatternChange={() =>
          setVisualPatternSelected((value) =>
            value === 'default' ? 'visualPattern' : 'default'
          )
        }
        data={data}
        range={{
          xAxisBottom: ['auto', 'auto'],
        }}
        elements={[
          { type: 'bar', id: 'Awareness' },
          { type: 'bar', id: 'Consideration' },
          { type: 'bar', id: 'Catalog sales' },
          { type: 'bar', id: 'Conversions' },
        ]}
        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
            )}`,
        }}
        modalZIndex={new FixedZIndex(11)}
      />
    </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 height="100%" width="100%" direction="column" gap={2}>
      <ChartGraph
        title="Performance over time"
        accessibilityLabel="Example of line chart"
        visualPatternSelected={visualPatternSelected}
        onVisualPatternChange={() =>
          setVisualPatternSelected((value) =>
            value === 'default' ? 'visualPattern' : 'default'
          )
        }
        data={data}
        range={{
          xAxisBottom: ['auto', 'auto'],
        }}
        elements={[
          { type: 'line', id: 'Engagement' },
          { type: 'line', id: 'Saves' },
          { type: 'line', id: 'Impressions' },
          { type: 'line', id: 'Page visits' },
        ]}
        type="line"
        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
            )}`,
        }}
        modalZIndex={new FixedZIndex(11)}
      />
    </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 height="100%" width="100%" direction="column" gap={2}>
      <ChartGraph
        title="Product group sales:"
        accessibilityLabel="Example of combo chart"
        visualPatternSelected={visualPatternSelected}
        onVisualPatternChange={() =>
          setVisualPatternSelected((value) =>
            value === 'default' ? 'visualPattern' : 'default'
          )
        }
        data={data}
        elements={[
          { type: 'bar', id: 'This year' },
          { type: 'bar', id: 'Profit' },
          { type: 'line', id: 'Last year' },
        ]}
        type="combo"
        tickFormatter={{
          yAxisLeft: (value) => {
            if (value >= 1000000) return `${value / 1000000}m`;
            if (value >= 1000) return `${value / 1000}k`;
            return value;
          },
        }}
        modalZIndex={new FixedZIndex(11)}
      />
    </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 height="100%" width="100%" direction="column" gap={2}>
      <ChartGraph
        title="Performance over last 30 days"
        accessibilityLabel="Example of line chart"
        visualPatternSelected={visualPatternSelected}
        onVisualPatternChange={() =>
          setVisualPatternSelected((value) =>
            value === 'default' ? 'visualPattern' : 'default'
          )
        }
        layout="verticalBiaxial"
        data={data}
        range={{ xAxisBottom: ['auto', 'auto'] }}
        elements={[
          { type: 'line', id: 'Spend', axis: 'left' },
          { type: 'line', id: 'Total ROAS (Checkout)', axis: 'right' },
        ]}
        type="line"
        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
            )}`,
        }}
        modalZIndex={new FixedZIndex(11)}
      />
    </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 height="100%" width="100%" direction="column" gap={2}>
      <ChartGraph
        accessibilityLabel="Example of chart with title and description"
        visualPatternSelected="disabled"
        onVisualPatternChange={() => {}}
        title="Clicks vs conversions"
        helpButton={
          <HelpButton
            accessibilityLabel="Click to learn more this ChartGraph"
            accessibilityPopoverLabel="Expanded information about this ChartGraph"
            text="If you want to learn more about Clicks vs conversions, visit our Help center."
            link={{
              href: 'https://help.pinterest.com/en/business/article/conversion-insights/',
              text: 'Read our documentation',
              accessibilityLabel: 'Visit our Help center',
            }}
          />
        }
        description="Includes both web and mobile"
        layout="verticalBiaxial"
        data={data}
        elements={[
          {
            type: 'bar',
            id: 'Clicks',
            axis: 'left',
          },
          {
            type: 'line',
            id: 'Conversions',
            axis: 'right',
          },
        ]}
      />
    </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 height="100%" width="100%" direction="column" gap={2}>
      <ChartGraph
        title="Views by demographics and device"
        accessibilityLabel="Example of chart with tooltip"
        visualPatternSelected={visualPatternSelected}
        onVisualPatternChange={() =>
          setVisualPatternSelected((value) =>
            value === 'default' ? 'visualPattern' : 'default'
          )
        }
        initialTicks={3}
        data={data}
        elements={[
          { type: 'bar', id: '18-30' },
          { type: 'bar', id: '30-50' },
          { type: 'bar', id: '50+' },
        ]}
        type="bar"
        tickFormatter={{
          yAxisLeft: (value) => {
            if (value >= 1000000) return `${value / 1000000}m`;
            if (value >= 1000) return `${value / 1000}k`;
            return value;
          },
        }}
      />
    </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 height="100%" width="100%" direction="column">
      <ChartGraph
        title="MAU per regions"
        accessibilityLabel="Example of chart with tooltip"
        visualPatternSelected={visualPatternSelected}
        onVisualPatternChange={() =>
          setVisualPatternSelected((value) =>
            value === 'default' ? 'visualPattern' : 'default'
          )
        }
        tickFormatter={{ yAxisLeft: (value) => `${value}m` }}
        initialTicks={3}
        type="bar"
        data={data}
        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 size="100" color="subtle">
                {label}
              </Text>
            </Flex>
          ) : null
        }
        elements={[
          {
            type: 'bar',
            id: '20-30',
          },
          {
            type: 'bar',
            id: '40-50',
          },
          {
            type: 'bar',
            id: '50-60',
          },
        ]}
      />
    </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 height="100%" width="100%" direction="column" gap={2}>
      <ChartGraph
        title="Average spend by region"
        accessibilityLabel="Example of chart with tooltip"
        visualPatternSelected={visualPatternSelected}
        onVisualPatternChange={() =>
          setVisualPatternSelected((value) =>
            value === 'default' ? 'visualPattern' : 'default'
          )
        }
        data={data}
        elements={[
          { type: 'bar', id: 'California' },
          { type: 'bar', id: 'Arizona' },
        ]}
        type="bar"
        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
            )}`,
        }}
        modalZIndex={new FixedZIndex(11)}
      />
    </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 height="100%" width="100%" direction="column" gap={2}>
      <ChartGraph
        title="Impressions over time"
        accessibilityLabel="Example of chart with reference area"
        visualPatternSelected="disabled"
        onVisualPatternChange={() => {}}
        data={data}
        range={{
          yAxisLeft: [0, 1000000],
          xAxisBottom: ['auto', new Date(2023, 0, 10).getTime()],
        }}
        elements={[{ type: 'line', id: 'Impressions' }]}
        type="line"
        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
            )}`,
        }}
        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,
          },
        ]}
      />
    </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 height="100%" width="100%" direction="column" gap={2}>
      <ChartGraph
        title="Forecast for 2023"
        accessibilityLabel="Example of time series chart"
        visualPatternSelected="disabled"
        onVisualPatternChange={() => {}}
        data={data}
        range={{
          xAxisBottom: ['auto', 'auto'],
        }}
        elements={[
          { type: 'line', id: 'Actual data', precision: 'exact' },
          { type: 'line', id: 'Forecast', precision: 'estimate' },
        ]}
        type="line"
        tickFormatter={{
          timeseries: (date) =>
            `${new Intl.DateTimeFormat('en-US', { month: 'short' }).format(
              date
            )}-${new Intl.DateTimeFormat('en-US', { day: '2-digit' }).format(
              date
            )}`,
        }}
        modalZIndex={new FixedZIndex(11)}
      />
    </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
          title="ChartGraph A"
          description="Color 01"
          accessibilityLabel="Example of line chart color 01"
          visualPatternSelected={visualPatternSelected}
          onVisualPatternChange={() =>
            setVisualPatternSelected((value) =>
              value === 'default' ? 'visualPattern' : 'default'
            )
          }
          initialTicks={3}
          type="line"
          renderTooltip="none"
          legend="none"
          data={data}
          elements={[{ type: 'line', id: 'Series_01' }]}
        />
        <ChartGraph
          title="ChartGraph B"
          description="Color 02"
          accessibilityLabel="Example of line chart color 02"
          visualPatternSelected={visualPatternSelected}
          onVisualPatternChange={() =>
            setVisualPatternSelected((value) =>
              value === 'default' ? 'visualPattern' : 'default'
            )
          }
          initialTicks={3}
          type="line"
          renderTooltip="none"
          legend="none"
          data={data}
          elements={[{ type: 'line', id: 'Series_01', color: '02' }]}
        />
      </Flex>
      <Flex>
        <ChartGraph
          title="ChartGraph C"
          description="Color 03"
          accessibilityLabel="Example of line chart color 03"
          visualPatternSelected={visualPatternSelected}
          onVisualPatternChange={() =>
            setVisualPatternSelected((value) =>
              value === 'default' ? 'visualPattern' : 'default'
            )
          }
          initialTicks={3}
          type="line"
          renderTooltip="none"
          legend="none"
          data={data}
          elements={[{ type: 'line', id: 'Series_01', color: '03' }]}
        />
        <ChartGraph
          title="ChartGraph D"
          description="Color 04"
          accessibilityLabel="Example of line chart color 04"
          visualPatternSelected={visualPatternSelected}
          onVisualPatternChange={() =>
            setVisualPatternSelected((value) =>
              value === 'default' ? 'visualPattern' : 'default'
            )
          }
          initialTicks={3}
          type="line"
          renderTooltip="none"
          legend="none"
          data={data}
          elements={[{ type: 'line', id: 'Series_01', color: '04' }]}
        />
      </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 height="100%" width="100%" direction="column" gap={2}>
      <Flex width="100%" justifyContent="between" wrap>
        <RadioGroup legend="ChartGraph type" direction="row" id="layout-type">
          <RadioGroup.RadioButton
            checked={type === 'bar'}
            id="layout-type-bar"
            label="Bar"
            name="bar"
            onChange={() => setType('bar')}
            value="bar"
            size="sm"
          />
          <RadioGroup.RadioButton
            checked={type === 'line'}
            id="layout-type-line"
            label="Line"
            name="line"
            onChange={() => setType('line')}
            value="line"
            size="sm"
          />
          <RadioGroup.RadioButton
            checked={type === 'combo'}
            id="layout-type-combo"
            label="Combo"
            name="combo"
            onChange={() => setType('combo')}
            value="combo"
            size="sm"
          />
        </RadioGroup>
        <RadioGroup legend="Layout" direction="row" id="layout_layout">
          <RadioGroup.RadioButton
            checked={layout === 'horizontal'}
            id="layout_layout-horizontal"
            label="Horizontal"
            name="horizontal"
            onChange={() => setLayout('horizontal')}
            value="horizontal"
            size="sm"
          />
          <RadioGroup.RadioButton
            checked={layout === 'horizontalBiaxial'}
            id="layout_layout-horizontalBiaxial"
            label="HorizontalBiaxial"
            name="horizontalBiaxial"
            onChange={() => setLayout('horizontalBiaxial')}
            value="horizontalBiaxial"
            size="sm"
          />
          <RadioGroup.RadioButton
            checked={layout === 'vertical'}
            id="layout_layout-vertical"
            label="Vertical"
            name="vertical"
            onChange={() => setLayout('vertical')}
            value="vertical"
            size="sm"
          />
          <RadioGroup.RadioButton
            checked={layout === 'verticalBiaxial'}
            id="layout_layout-verticalBiaxial"
            label="VerticalBiaxial"
            name="verticalBiaxial"
            onChange={() => setLayout('verticalBiaxial')}
            value="verticalBiaxial"
            size="sm"
          />
        </RadioGroup>
      </Flex>

      <ChartGraph
        title="ChartGraph"
        titleDisplay="hidden"
        accessibilityLabel="Example of chart with decal custom dimension"
        visualPatternSelected={visualPatternSelected}
        onVisualPatternChange={() =>
          setVisualPatternSelected((value) =>
            value === 'default' ? 'visualPattern' : 'default'
          )
        }
        layout={layout}
        type={type}
        legend="none"
        data={data}
        elements={[
          {
            type: type === 'combo' ? 'bar' : type,
            id: 'Series_01',
            axis: axisSeries01,
          },
          {
            type: type === 'combo' ? 'line' : type,
            id: 'Series_02',
            axis: axisSeries02,
          },
        ]}
      />
    </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 height="100%" width="100%" direction="column" gap={2}>
      <RadioGroup legend="ChartGraph type" direction="row" id="range">
        <RadioGroup.RadioButton
          checked={type === 'bar'}
          id="range-bar"
          label="Bar"
          name="bar"
          onChange={() => setType('bar')}
          value="bar"
          size="sm"
        />
        <RadioGroup.RadioButton
          checked={type === 'line'}
          id="range-line"
          label="Line"
          name="line"
          onChange={() => setType('line')}
          value="line"
          size="sm"
        />
        <RadioGroup.RadioButton
          checked={type === 'combo'}
          id="range-combo"
          label="Combo"
          name="combo"
          onChange={() => setType('combo')}
          value="combo"
          size="sm"
        />
      </RadioGroup>
      <ChartGraph
        title="ChartGraph"
        titleDisplay="hidden"
        accessibilityLabel="Example of range in charts"
        visualPatternSelected={visualPatternSelected}
        onVisualPatternChange={() =>
          setVisualPatternSelected((value) =>
            value === 'default' ? 'visualPattern' : 'default'
          )
        }
        layout="verticalBiaxial"
        type={type}
        data={data}
        range={{ yAxisLeft: [0, 100] }}
        elements={[
          {
            type: type === 'combo' ? 'bar' : type,
            id: 'Percentage',
            axis: 'left',
          },
          {
            type: type === 'combo' ? 'line' : type,
            id: 'Absolute',
            axis: 'right',
          },
        ]}
      />
    </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 width="100%" justifyContent="between">
          <RadioGroup
            legend="ChartGraph type"
            direction="row"
            id="responsive-type"
          >
            <RadioGroup.RadioButton
              checked={type === 'bar'}
              id="responsive-type-bar"
              label="Bar"
              name="bar"
              onChange={() => setType('bar')}
              value="bar"
              size="sm"
            />
            <RadioGroup.RadioButton
              checked={type === 'line'}
              id="responsive-type-line"
              label="Line"
              name="line"
              onChange={() => setType('line')}
              value="line"
              size="sm"
            />
            <RadioGroup.RadioButton
              checked={type === 'combo'}
              id="responsive-type-combo"
              label="Combo"
              name="combo"
              onChange={() => setType('combo')}
              value="combo"
              size="sm"
            />
          </RadioGroup>
          <RadioGroup legend="Layout" direction="row" id="responsive_layout">
            <RadioGroup.RadioButton
              checked={layout === 'horizontal'}
              id="responsive_layout-horizontal"
              label="Horizontal"
              name="horizontal"
              onChange={() => setLayout('horizontal')}
              value="horizontal"
              size="sm"
            />
            <RadioGroup.RadioButton
              checked={layout === 'horizontalBiaxial'}
              id="responsive_layout-horizontalBiaxial"
              label="HorizontalBiaxial"
              name="horizontalBiaxial"
              onChange={() => setLayout('horizontalBiaxial')}
              value="horizontalBiaxial"
              size="sm"
            />
            <RadioGroup.RadioButton
              checked={layout === 'vertical'}
              id="responsive_layout-vertical"
              label="Vertical"
              name="vertical"
              onChange={() => setLayout('vertical')}
              value="vertical"
              size="sm"
            />
            <RadioGroup.RadioButton
              checked={layout === 'verticalBiaxial'}
              id="responsive_layout-verticalBiaxial"
              label="VerticalBiaxial"
              name="verticalBiaxial"
              onChange={() => setLayout('verticalBiaxial')}
              value="verticalBiaxial"
              size="sm"
            />
          </RadioGroup>
        </Flex>
        <Flex alignItems="center" direction="column">
          <Flex>
            <Label htmlFor={labelId}>
              <Text>Container Width</Text>
            </Label>
            <Text>{`: ${width}px`}</Text>
          </Flex>
          <input
            id={labelId}
            type="range"
            defaultValue={800}
            onChange={updateWidth}
            min={200}
            max={800}
            step={1}
            style={{ width: '400px', display: 'block', margin: '10px auto' }}
          />
        </Flex>

        <div
          ref={(el) => {
            scrollContainerRef.current = el;
          }}
          style={{
            height: '300px',
            margin: '0 auto',
            outline: '3px solid #ddd',
            overflowY: 'scroll',
            width: `${width}px`,
          }}
        >
          <ChartGraph
            title="ChartGraph"
            titleDisplay="hidden"
            accessibilityLabel="Example of chart with decal custom dimension"
            visualPatternSelected={visualPatternSelected}
            onVisualPatternChange={() =>
              setVisualPatternSelected((value) =>
                value === 'default' ? 'visualPattern' : 'default'
              )
            }
            layout={layout}
            type={type}
            legend="none"
            data={data}
            elements={[
              {
                type: type === 'combo' ? 'bar' : type,
                id: 'Series_01',
                axis: axisSeries01,
              },
              {
                type: type === 'combo' ? 'line' : type,
                id: 'Series_02',
                axis: axisSeries02,
              },
            ]}
          />
        </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"
      visualPatternSelected="disabled"
      onVisualPatternChange={() => {}}
      type="bar"
      title="Title"
      initialTicks={3}
      legend="none"
      description="Description"
      data={selectedId === '01' ? dataA : dataB}
      elements={[
        {
          type: 'bar',
          id: selectedId === '01' ? 'Impressions' : 'Engagement',
          color: getColor(selectedId || '01'),
        },
      ]}
    >
      <TileData
        id="01"
        color="01"
        title="Impressions"
        value="10M"
        selected={isSelected('01')}
        onTap={({ id }) => setSelectedId(id)}
        trend={{ value: 29, accessibilityLabel: 'Trending up' }}
      />
      <TileData
        id="02"
        color="02"
        title="Engagement"
        value="2M"
        selected={isSelected('02')}
        onTap={({ id }) => setSelectedId(id)}
        trend={{ value: 29, accessibilityLabel: 'Trending up' }}
      />
    </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"
      visualPatternSelected={visualPatternSelected}
      initialTicks={3}
      onVisualPatternChange={() =>
        setVisualPatternSelected((value) =>
          value === 'default' ? 'visualPattern' : 'default'
        )
      }
      type="bar"
      title="Clickthroughs per region"
      legend="none"
      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}
    >
      <TagData
        id="Campaign Autumn"
        color="01"
        text="Campaign Autumn"
        selected={isSelected('Campaign Autumn')}
        onTap={({ id, selected }) => handleSelectors({ id, selected })}
        showCheckbox
      />
      <TagData
        id="Campaign Winter"
        color="02"
        text="Campaign Winter"
        selected={isSelected('Campaign Winter')}
        onTap={({ id, selected }) => handleSelectors({ id, selected })}
        showCheckbox
      />
      <TagData
        id="Campaign Spring"
        color="03"
        text="Campaign Spring"
        selected={isSelected('Campaign Spring')}
        onTap={({ id, selected }) => handleSelectors({ id, selected })}
        showCheckbox
      />
    </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
      title="ChartGraph"
      titleDisplay="hidden"
      accessibilityLabel="Example of Bar chart"
      visualPatternSelected="disabled"
      onVisualPatternChange={() => {}}
      type="bar"
      legend="none"
      data={data}
      tickFormatter={{ yAxisLeft: (value) => `${value / 1000000}M` }}
      elements={[{ type: 'bar', id: 'Series_01' }]}
    />
  );
}

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