Getting Started with Victory
Victory is an opinionated, but fully overridable, ecosystem of composable React components for building interactive data visualizations. The following tutorial explains how to set up a basic chart.
const series = [ { name: "Canada", data: [ 3.9670002, 5.2650003, 6.201, 7.8010006, 9.694, 11.214001, 11.973001, 12.250001, 12.816001, 13.413001, 13.626961, 14.30356, 15.295461, ], }, { name: "Germany", data: [ 26.903002, 28.712002, 30.979002, 33.477, 38.614002, 44.58, 49.435, 55.58, 58.721004, 60.742004, 62.201004, 63.833004, 66.315, ], }, { name: "India", data: [ 13.184001, 16.179, 17.2997, 18.4204, 22.465302, 25.08819, 28.700441, 32.84846, 35.288105, 37.50518, 38.558605, 40.06727, 41.929783, ], }, { name: "United States", data: [ 39.349697, 45.79497, 59.453304, 60.198166, 64.43019, 72.767235, 81.502365, 87.83079, 94.66619, 103.835556, 118.66354, 133.01929, 140.86162, ], }, { name: "Italy", data: [ 5.794, 6.918, 8.102, 8.542001, 8.683001, 9.137, 9.384001, 9.736579, 10.230247, 10.679461, 10.870623, 11.253734, 11.779734, ], }, { name: "Japan", data: [ 2.2940001, 2.4190001, 2.562, 2.6460001, 2.753, 2.808, 3.2470002, 3.4830003, 3.4980001, 3.9540002, 4.367, 4.467, 4.577, ], }, { name: "Spain", data: [ 20.693, 21.529001, 22.789001, 22.958, 22.925001, 22.943, 22.990002, 23.12448, 23.405056, 25.590076, 26.819191, 27.907652, 29.307837, ], }, ]; const symbols = [ "circle", "diamond", "plus", "square", "triangleUp", ]; function App() { return ( <VictoryChart padding={{ top: 50, left: 70, right: 50, bottom: 100, }} theme={VictoryTheme.clean} > <VictoryLabel text="Installed Wind Capacity (GW)" style={{ ...VictoryTheme.clean.label, fontSize: 10, }} dx={28} dy={18} /> <VictoryLabel text="International Renewable Energy Agency (2023)" style={{ ...VictoryTheme.clean.label, fontSize: 8, }} dx={28} dy={30} /> <VictoryAxis tickValues={_.range( 2010, 2024, 2, )} style={{ tickLabels: { fontSize: 8, }, ticks: { stroke: "#757575", size: 5, }, }} /> <VictoryAxis dependentAxis label="Total Capacity" tickValues={_.range(0, 200, 40)} tickFormat={(value) => `${value} GW` } style={{ axis: { stroke: "transparent", }, axisLabel: { fontSize: 8, padding: 50, }, tickLabels: { fontSize: 8, }, grid: { stroke: "#d9d9d9", size: 5, }, }} /> {series.map((s, i) => ( <VictoryGroup data={s.data.map((d, i) => ({ x: i + 2010, y: d, }))} key={s.name} > <VictoryLine style={{ data: { stroke: VictoryTheme.clean .palette .qualitative[i], strokeWidth: 1, }, }} /> <VictoryScatter size={2} symbol={symbols[i]} style={{ data: { fill: VictoryTheme.clean .palette.qualitative[ i ], }, }} /> </VictoryGroup> ))} <VictoryLegend itemsPerRow={4} x={50} y={220} data={series.map((s) => ({ name: s.name, symbol: { fill: VictoryTheme.clean .palette.qualitative[ series.indexOf(s) ], type: symbols[ series.indexOf(s) ], }, }))} style={{ data: { fill: ({ datum }) => datum.symbol.fill, }, labels: { fontSize: 8, }, border: { stroke: "transparent", }, }} /> <Line x1={25} x2={425} y1={270} y2={270} style={{ stroke: "#d9d9d9", }} /> <VictoryLabel text={[ "Hannah Ritchie, Max Roser and Pablo Rosado (2020) - “Renewable Energy”", "https://ourworldindata.org/renewable-energy", ]} style={{ fontSize: 8, fontWeight: 300, fontFamily: "Inter", }} dx={30} dy={285} theme={VictoryTheme.clean} /> </VictoryChart> ); } render(<App />);
Tutorial
In this guide, we’ll show you how to get started with Victory and walk you through the creation and customization of a composed chart.
1. Import Victory
Add Victory to your project with the command npm install victory, then import it into your component. For now, let's start with a simple Line Chart.
import React from "react";
import {
  VictoryChart,
  VictoryLine,
} from "victory";
2. Start with a basic chart
Components include sensible defaults, so even without data the chart will render with samples. To add some basic styling, we will use our built in clean theme. When a theme is applied to VictoryChart, it will be inherited by all child components.
function App() { return ( <VictoryChart theme={VictoryTheme.clean} > <VictoryLine /> </VictoryChart> ); } render(<App />);
3. Add your data
Let's add some data. Victory cartesian charts look for x and y values in data points, which our data doesn't have. We can work around this by adding accessor props to our VictoryLine component. Our data contains the country name, and an array of values from 2010 to 2022.
type Data = {
  name: string;
  data: number[];
};
const series = [ { name: "Canada", data: [ 3.9670002, 5.2650003, 6.201, 7.8010006, 9.694, 11.214001, 11.973001, 12.250001, 12.816001, 13.413001, 13.626961, 14.30356, 15.295461, ], }, ]; function App() { return ( <VictoryChart theme={VictoryTheme.clean} > <VictoryLine data={series[0].data.map( (d, i) => ({ x: i + 2010, y: d, }), )} /> </VictoryChart> ); } render(<App />);
4. Customize the X axis
VictoryChart is a wrapper component that plots all of its children on the same scale. The default axes may not always be what you need, but they can be customized with VictoryAxis components.
We will start by adding a dependent axis to our chart. This axis will be the vertical axis, and we will customize it with a label and tick values.
<VictoryAxis
  dependentAxis
  tickValues={_.range(0, 200, 40)}
  tickFormat={(value) => `${value} GW`}
/>
Our data is an array of values from the year 2010 to 2022, so we will also add a horizontal axis with tick values.
<VictoryAxis
  tickValues={_.range(2010, 2024, 2)}
/>
Finally, we will customize the axis styles to make it more readable. We will adjust the font size of the tick labels and the axis ticks, and add grid lines to the dependent axis.
const series = [ { name: "Canada", data: [ 3.9670002, 5.2650003, 6.201, 7.8010006, 9.694, 11.214001, 11.973001, 12.250001, 12.816001, 13.413001, 13.626961, 14.30356, 15.295461, ], }, ]; function App() { return ( <VictoryChart theme={VictoryTheme.clean} > <VictoryAxis tickValues={_.range( 2010, 2024, 2, )} style={{ tickLabels: { fontSize: 8, }, ticks: { stroke: "#757575", size: 5, }, }} /> <VictoryAxis dependentAxis tickValues={_.range(0, 200, 40)} tickFormat={(value) => `${value} GW` } style={{ axis: { stroke: "transparent", }, axisLabel: { fontSize: 8, padding: 50, }, tickLabels: { fontSize: 8, }, grid: { stroke: "#d9d9d9", size: 5, }, }} /> <VictoryLine data={series[0].data.map( (d, i) => ({ x: i + 2010, y: d, }), )} /> </VictoryChart> ); } render(<App />);
5. Multiple Series
Lets expand our data to include multiple countries by iterating over the series array and adding a VictoryLine component for each country. Since we need to identify the unique trend lines, we will also adjust the stroke color of each line.
{
  series.map((s, i) => (
    <VictoryLine
      key={s.name}
      data={s.data.map((d, i) => ({
        x: i + 2010,
        y: d,
      }))}
      style={{
        data: {
          stroke:
            VictoryTheme.clean.palette
              .qualitative[i],
          strokeWidth: 1,
        },
      }}
    />
  ));
}
const series = [ { name: "Canada", data: [ 3.9670002, 5.2650003, 6.201, 7.8010006, 9.694, 11.214001, 11.973001, 12.250001, 12.816001, 13.413001, 13.626961, 14.30356, 15.295461, ], }, { name: "Germany", data: [ 26.903002, 28.712002, 30.979002, 33.477, 38.614002, 44.58, 49.435, 55.58, 58.721004, 60.742004, 62.201004, 63.833004, 66.315, ], }, { name: "India", data: [ 13.184001, 16.179, 17.2997, 18.4204, 22.465302, 25.08819, 28.700441, 32.84846, 35.288105, 37.50518, 38.558605, 40.06727, 41.929783, ], }, { name: "United States", data: [ 39.349697, 45.79497, 59.453304, 60.198166, 64.43019, 72.767235, 81.502365, 87.83079, 94.66619, 103.835556, 118.66354, 133.01929, 140.86162, ], }, { name: "Italy", data: [ 5.794, 6.918, 8.102, 8.542001, 8.683001, 9.137, 9.384001, 9.736579, 10.230247, 10.679461, 10.870623, 11.253734, 11.779734, ], }, { name: "Japan", data: [ 2.2940001, 2.4190001, 2.562, 2.6460001, 2.753, 2.808, 3.2470002, 3.4830003, 3.4980001, 3.9540002, 4.367, 4.467, 4.577, ], }, { name: "Spain", data: [ 20.693, 21.529001, 22.789001, 22.958, 22.925001, 22.943, 22.990002, 23.12448, 23.405056, 25.590076, 26.819191, 27.907652, 29.307837, ], }, ]; function App() { return ( <VictoryChart theme={VictoryTheme.clean} > <VictoryAxis tickValues={_.range( 2010, 2024, 2, )} style={{ tickLabels: { fontSize: 8, }, ticks: { stroke: "#757575", size: 5, }, }} /> <VictoryAxis dependentAxis tickValues={_.range(0, 200, 40)} tickFormat={(value) => `${value} GW` } style={{ axis: { stroke: "transparent", }, axisLabel: { fontSize: 8, padding: 50, }, tickLabels: { fontSize: 8, }, grid: { stroke: "#d9d9d9", size: 5, }, }} /> {series.map((s, i) => ( <VictoryLine style={{ data: { stroke: VictoryTheme.clean .palette.qualitative[ i ], strokeWidth: 1, }, }} data={s.data.map((d, i) => ({ x: i + 2010, y: d, }))} key={s.name} /> ))} </VictoryChart> ); } render(<App />);
6. Combining Chart Types
To make each data point stand out more, we can combine our VictoryLine chart with a VictoryScatter chart. Victory provides a specialized wrapper component VictoryGroup that helps us apply properties to multiple components at once.
{
  series.map((s, i) => (
    <VictoryGroup
      data={s.data.map((d, i) => ({
        x: i + 2010,
        y: d,
      }))}
      key={s.name}
    >
      <VictoryLine
        style={{
          data: {
            stroke:
              VictoryTheme.clean.palette
                .qualitative[i],
            strokeWidth: 1,
          },
        }}
      />
      <VictoryScatter
        size={2}
        symbol={symbols[i]}
        style={{
          data: {
            fill: VictoryTheme.clean
              .palette.qualitative[i],
          },
        }}
      />
    </VictoryGroup>
  ));
}
const series = [ { name: "Canada", data: [ 3.9670002, 5.2650003, 6.201, 7.8010006, 9.694, 11.214001, 11.973001, 12.250001, 12.816001, 13.413001, 13.626961, 14.30356, 15.295461, ], }, { name: "Germany", data: [ 26.903002, 28.712002, 30.979002, 33.477, 38.614002, 44.58, 49.435, 55.58, 58.721004, 60.742004, 62.201004, 63.833004, 66.315, ], }, { name: "India", data: [ 13.184001, 16.179, 17.2997, 18.4204, 22.465302, 25.08819, 28.700441, 32.84846, 35.288105, 37.50518, 38.558605, 40.06727, 41.929783, ], }, { name: "United States", data: [ 39.349697, 45.79497, 59.453304, 60.198166, 64.43019, 72.767235, 81.502365, 87.83079, 94.66619, 103.835556, 118.66354, 133.01929, 140.86162, ], }, { name: "Italy", data: [ 5.794, 6.918, 8.102, 8.542001, 8.683001, 9.137, 9.384001, 9.736579, 10.230247, 10.679461, 10.870623, 11.253734, 11.779734, ], }, { name: "Japan", data: [ 2.2940001, 2.4190001, 2.562, 2.6460001, 2.753, 2.808, 3.2470002, 3.4830003, 3.4980001, 3.9540002, 4.367, 4.467, 4.577, ], }, { name: "Spain", data: [ 20.693, 21.529001, 22.789001, 22.958, 22.925001, 22.943, 22.990002, 23.12448, 23.405056, 25.590076, 26.819191, 27.907652, 29.307837, ], }, ]; const symbols = [ "circle", "diamond", "plus", "square", "triangleUp", ]; function App() { return ( <VictoryChart theme={VictoryTheme.clean} > <VictoryAxis tickValues={_.range( 2010, 2024, 2, )} style={{ tickLabels: { fontSize: 8, }, ticks: { stroke: "#757575", size: 5, }, }} /> <VictoryAxis dependentAxis tickValues={_.range(0, 200, 40)} tickFormat={(value) => `${value} GW` } style={{ axis: { stroke: "transparent", }, axisLabel: { fontSize: 8, padding: 50, }, tickLabels: { fontSize: 8, }, grid: { stroke: "#d9d9d9", size: 5, }, }} /> {series.map((s, i) => ( <VictoryGroup data={s.data.map((d, i) => ({ x: i + 2010, y: d, }))} key={s.name} > <VictoryLine style={{ data: { stroke: VictoryTheme.clean .palette .qualitative[i], strokeWidth: 1, }, }} /> <VictoryScatter size={2} symbol={symbols[i]} style={{ data: { fill: VictoryTheme.clean .palette.qualitative[ i ], }, }} /> </VictoryGroup> ))} </VictoryChart> ); } render(<App />);
7. Adding a Legend
To make it easier to identify each country, we can add a legend to our chart. Victory provides a VictoryLegend component that can be used to display a legend for the chart.
<VictoryLegend
  itemsPerRow={4}
  x={50}
  y={220}
  data={series.map((s, i) => ({
    name: s.name,
    symbol: {
      fill: VictoryTheme.clean.palette
        .qualitative[i],
      type: symbols[i],
    },
  }))}
/>
Since our Legend is going to take up some space in our chart, we also need to adjust the padding to provide enough space for the legend.
<VictoryChart
  padding={{
    top: 50,
    left: 70,
    right: 50,
    bottom: 100,
  }}
/>
const series = [ { name: "Canada", data: [ 3.9670002, 5.2650003, 6.201, 7.8010006, 9.694, 11.214001, 11.973001, 12.250001, 12.816001, 13.413001, 13.626961, 14.30356, 15.295461, ], }, { name: "Germany", data: [ 26.903002, 28.712002, 30.979002, 33.477, 38.614002, 44.58, 49.435, 55.58, 58.721004, 60.742004, 62.201004, 63.833004, 66.315, ], }, { name: "India", data: [ 13.184001, 16.179, 17.2997, 18.4204, 22.465302, 25.08819, 28.700441, 32.84846, 35.288105, 37.50518, 38.558605, 40.06727, 41.929783, ], }, { name: "United States", data: [ 39.349697, 45.79497, 59.453304, 60.198166, 64.43019, 72.767235, 81.502365, 87.83079, 94.66619, 103.835556, 118.66354, 133.01929, 140.86162, ], }, { name: "Italy", data: [ 5.794, 6.918, 8.102, 8.542001, 8.683001, 9.137, 9.384001, 9.736579, 10.230247, 10.679461, 10.870623, 11.253734, 11.779734, ], }, { name: "Japan", data: [ 2.2940001, 2.4190001, 2.562, 2.6460001, 2.753, 2.808, 3.2470002, 3.4830003, 3.4980001, 3.9540002, 4.367, 4.467, 4.577, ], }, { name: "Spain", data: [ 20.693, 21.529001, 22.789001, 22.958, 22.925001, 22.943, 22.990002, 23.12448, 23.405056, 25.590076, 26.819191, 27.907652, 29.307837, ], }, ]; const symbols = [ "circle", "diamond", "plus", "square", "triangleUp", ]; function App() { return ( <VictoryChart padding={{ top: 50, left: 70, right: 50, bottom: 100, }} theme={VictoryTheme.clean} > <VictoryAxis tickValues={_.range( 2010, 2024, 2, )} style={{ tickLabels: { fontSize: 8, }, ticks: { stroke: "#757575", size: 5, }, }} /> <VictoryAxis dependentAxis tickValues={_.range(0, 200, 40)} tickFormat={(value) => `${value} GW` } style={{ axis: { stroke: "transparent", }, axisLabel: { fontSize: 8, padding: 50, }, tickLabels: { fontSize: 8, }, grid: { stroke: "#d9d9d9", size: 5, }, }} /> {series.map((s, i) => ( <VictoryGroup data={s.data.map((d, i) => ({ x: i + 2010, y: d, }))} key={s.name} > <VictoryLine style={{ data: { stroke: VictoryTheme.clean .palette .qualitative[i], strokeWidth: 1, }, }} /> <VictoryScatter size={2} symbol={symbols[i]} style={{ data: { fill: VictoryTheme.clean .palette.qualitative[ i ], }, }} /> </VictoryGroup> ))} <VictoryLegend itemsPerRow={4} x={50} y={220} data={series.map((s) => ({ name: s.name, symbol: { fill: VictoryTheme.clean .palette.qualitative[ series.indexOf(s) ], type: symbols[ series.indexOf(s) ], }, }))} style={{ data: { fill: ({ datum }) => datum.symbol.fill, }, labels: { fontSize: 8, }, border: { stroke: "transparent", }, }} /> </VictoryChart> ); } render(<App />);
8. Adding Labels
Finally, we can add labels to our chart to provide more context. We will add a title and a source link to our chart as well as axes labels.
<VictoryLabel
  text="Installed Wind Capacity (GW)"
  dx={28}
  dy={18}
/>
const series = [ { name: "Canada", data: [ 3.9670002, 5.2650003, 6.201, 7.8010006, 9.694, 11.214001, 11.973001, 12.250001, 12.816001, 13.413001, 13.626961, 14.30356, 15.295461, ], }, { name: "Germany", data: [ 26.903002, 28.712002, 30.979002, 33.477, 38.614002, 44.58, 49.435, 55.58, 58.721004, 60.742004, 62.201004, 63.833004, 66.315, ], }, { name: "India", data: [ 13.184001, 16.179, 17.2997, 18.4204, 22.465302, 25.08819, 28.700441, 32.84846, 35.288105, 37.50518, 38.558605, 40.06727, 41.929783, ], }, { name: "United States", data: [ 39.349697, 45.79497, 59.453304, 60.198166, 64.43019, 72.767235, 81.502365, 87.83079, 94.66619, 103.835556, 118.66354, 133.01929, 140.86162, ], }, { name: "Italy", data: [ 5.794, 6.918, 8.102, 8.542001, 8.683001, 9.137, 9.384001, 9.736579, 10.230247, 10.679461, 10.870623, 11.253734, 11.779734, ], }, { name: "Japan", data: [ 2.2940001, 2.4190001, 2.562, 2.6460001, 2.753, 2.808, 3.2470002, 3.4830003, 3.4980001, 3.9540002, 4.367, 4.467, 4.577, ], }, { name: "Spain", data: [ 20.693, 21.529001, 22.789001, 22.958, 22.925001, 22.943, 22.990002, 23.12448, 23.405056, 25.590076, 26.819191, 27.907652, 29.307837, ], }, ]; const symbols = [ "circle", "diamond", "plus", "square", "triangleUp", ]; function App() { return ( <VictoryChart padding={{ top: 50, left: 70, right: 50, bottom: 100, }} theme={VictoryTheme.clean} > <VictoryLabel text="Installed Wind Capacity (GW)" style={{ ...VictoryTheme.clean.label, fontSize: 10, }} dx={28} dy={18} /> <VictoryLabel text="International Renewable Energy Agency (2023)" style={{ ...VictoryTheme.clean.label, fontSize: 8, }} dx={28} dy={30} /> <VictoryAxis tickValues={_.range( 2010, 2024, 2, )} style={{ tickLabels: { fontSize: 8, }, ticks: { stroke: "#757575", size: 5, }, }} /> <VictoryAxis dependentAxis label="Total Capacity" tickValues={_.range(0, 200, 40)} tickFormat={(value) => `${value} GW` } style={{ axis: { stroke: "transparent", }, axisLabel: { fontSize: 8, padding: 50, }, tickLabels: { fontSize: 8, }, grid: { stroke: "#d9d9d9", size: 5, }, }} /> {series.map((s, i) => ( <VictoryGroup data={s.data.map((d, i) => ({ x: i + 2010, y: d, }))} key={s.name} > <VictoryLine style={{ data: { stroke: VictoryTheme.clean .palette .qualitative[i], strokeWidth: 1, }, }} /> <VictoryScatter size={2} symbol={symbols[i]} style={{ data: { fill: VictoryTheme.clean .palette.qualitative[ i ], }, }} /> </VictoryGroup> ))} <VictoryLegend itemsPerRow={4} x={50} y={220} data={series.map((s) => ({ name: s.name, symbol: { fill: VictoryTheme.clean .palette.qualitative[ series.indexOf(s) ], type: symbols[ series.indexOf(s) ], }, }))} style={{ data: { fill: ({ datum }) => datum.symbol.fill, }, labels: { fontSize: 8, }, border: { stroke: "transparent", }, }} /> <Line x1={25} x2={425} y1={270} y2={270} style={{ stroke: "#d9d9d9", }} /> <VictoryLabel text={[ "Hannah Ritchie, Max Roser and Pablo Rosado (2020) - “Renewable Energy”", "https://ourworldindata.org/renewable-energy", ]} style={{ fontSize: 8, fontWeight: 300, fontFamily: "Inter", }} dx={30} dy={285} theme={VictoryTheme.clean} /> </VictoryChart> ); } render(<App />);
Next Steps
Congratulations! You’ve created your first chart with Victory. Happy charting.
Documentation, Contributing, and Source
For more information about Victory and its components, check out the docs - see VictoryChart to get started. Interested in helping out or seeing what's happening under the hood? Victory is maintained at github.com/FormidableLabs/victory, and you can start contributing here.