Search code examples
javascriptreactjschartsvictory-charts

How to change font type in React Victory Chart?


I'm really new with formidable's Victory components for modular charting and data visualization. For the last few days I'm trying to change the font to "Poppins font" in my Victory Bar component. But it's not seems possible with my very little knowledge. It's showing "Helvetica Neue" fonts. Which is default font of Victory. Victory's documentation has no clear instruction on changing font type.

Is there any way to solve this?

Here is my code. I'm using React with NextJS.

import {
  VictoryBar,
  VictoryChart,
  VictoryTheme,
  VictoryTooltip,
} from "victory";

const data = [
  { reptile: "lizard", awesomeness: 3234 },
  { reptile: "snake", awesomeness: 2048 },
  { reptile: "crocodile", awesomeness: 2600 },
  { reptile: "alligator", awesomeness: 9000 },
];
const x = "reptile";
const y = "awesomeness";

function Victory() {
  return (
    <div>
      <VictoryChart theme={VictoryTheme.material} domainPadding={{ x: 20 }}>
        <VictoryBar
          style={{ data: { fill: "#2563eb" } }}
          data={data}
          x={x}
          y={y}
          alignment="start"
          labels={({ datum }) => datum.awesomeness}
          labelComponent={<VictoryTooltip />}
        />
      </VictoryChart>
    </div>
  );
}

export default Victory;

Solution

  • One way of solving this problem could be to create a custom theme instead of using theme={VictoryTheme.material} for example. This could be done like this:

    import { assign } from "lodash";
    
    // *
    // * Colors
    // *
    const yellow200 = "#FFF59D";
    const deepOrange600 = "#F4511E";
    const lime300 = "#DCE775";
    const lightGreen500 = "#8BC34A";
    const teal700 = "#00796B";
    const cyan900 = "#006064";
    const colors = [
      deepOrange600,
      yellow200,
      lime300,
      lightGreen500,
      teal700,
      cyan900
    ];
    const blueGrey50 = "#ECEFF1";
    const blueGrey300 = "#90A4AE";
    const blueGrey700 = "#455A64";
    const grey900 = "#212121";
    // *
    // * Typography
    // *
    const sansSerif = "'Poppins', sans-serif";
    const letterSpacing = "normal";
    const fontSize = 12;
    // *
    // * Layout
    // *
    const padding = 8;
    const baseProps = {
      width: 350,
      height: 350,
      padding: 50
    };
    // *
    // * Labels
    // *
    const baseLabelStyles = {
      fontFamily: sansSerif,
      fontSize,
      letterSpacing,
      padding,
      fill: blueGrey700,
      stroke: "transparent",
      strokeWidth: 0
    };
    
    const centeredLabelStyles = assign({ textAnchor: "middle" }, baseLabelStyles);
    // *
    // * Strokes
    // *
    const strokeDasharray = "10, 5";
    const strokeLinecap = "round";
    const strokeLinejoin = "round";
    
    export const custom = {
      area: assign(
        {
          style: {
            data: {
              fill: grey900
            },
            labels: baseLabelStyles
          }
        },
        baseProps
      ),
      axis: assign(
        {
          style: {
            axis: {
              fill: "transparent",
              stroke: blueGrey300,
              strokeWidth: 2,
              strokeLinecap,
              strokeLinejoin
            },
            axisLabel: assign({}, centeredLabelStyles, {
              padding,
              stroke: "transparent"
            }),
            grid: {
              fill: "none",
              stroke: blueGrey50,
              strokeDasharray,
              strokeLinecap,
              strokeLinejoin,
              pointerEvents: "painted"
            },
            ticks: {
              fill: "transparent",
              size: 5,
              stroke: blueGrey300,
              strokeWidth: 1,
              strokeLinecap,
              strokeLinejoin
            },
            tickLabels: assign({}, baseLabelStyles, {
              fill: blueGrey700
            })
          }
        },
        baseProps
      ),
      polarDependentAxis: assign({
        style: {
          ticks: {
            fill: "transparent",
            size: 1,
            stroke: "transparent"
          }
        }
      }),
      bar: assign(
        {
          style: {
            data: {
              fill: blueGrey700,
              padding,
              strokeWidth: 0
            },
            labels: baseLabelStyles
          }
        },
        baseProps
      ),
      boxplot: assign(
        {
          style: {
            max: { padding, stroke: blueGrey700, strokeWidth: 1 },
            maxLabels: assign({}, baseLabelStyles, { padding: 3 }),
            median: { padding, stroke: blueGrey700, strokeWidth: 1 },
            medianLabels: assign({}, baseLabelStyles, { padding: 3 }),
            min: { padding, stroke: blueGrey700, strokeWidth: 1 },
            minLabels: assign({}, baseLabelStyles, { padding: 3 }),
            q1: { padding, fill: blueGrey700 },
            q1Labels: assign({}, baseLabelStyles, { padding: 3 }),
            q3: { padding, fill: blueGrey700 },
            q3Labels: assign({}, baseLabelStyles, { padding: 3 })
          },
          boxWidth: 20
        },
        baseProps
      ),
      candlestick: assign(
        {
          style: {
            data: {
              stroke: blueGrey700
            },
            labels: assign({}, baseLabelStyles, { padding: 5 })
          },
          candleColors: {
            positive: "#ffffff",
            negative: blueGrey700
          }
        },
        baseProps
      ),
      chart: baseProps,
      errorbar: assign(
        {
          borderWidth: 8,
          style: {
            data: {
              fill: "transparent",
              opacity: 1,
              stroke: blueGrey700,
              strokeWidth: 2
            },
            labels: baseLabelStyles
          }
        },
        baseProps
      ),
      group: assign(
        {
          colorScale: colors
        },
        baseProps
      ),
      histogram: assign(
        {
          style: {
            data: {
              fill: blueGrey700,
              stroke: grey900,
              strokeWidth: 2
            },
            labels: baseLabelStyles
          }
        },
        baseProps
      ),
      legend: {
        colorScale: colors,
        gutter: 10,
        orientation: "vertical",
        titleOrientation: "top",
        style: {
          data: {
            type: "circle"
          },
          labels: baseLabelStyles,
          title: assign({}, baseLabelStyles, { padding: 5 })
        }
      },
      line: assign(
        {
          style: {
            data: {
              fill: "transparent",
              opacity: 1,
              stroke: blueGrey700,
              strokeWidth: 2
            },
            labels: baseLabelStyles
          }
        },
        baseProps
      ),
      pie: assign(
        {
          colorScale: colors,
          style: {
            data: {
              padding,
              stroke: blueGrey50,
              strokeWidth: 1
            },
            labels: assign({}, baseLabelStyles, { padding: 20 })
          }
        },
        baseProps
      ),
      scatter: assign(
        {
          style: {
            data: {
              fill: blueGrey700,
              opacity: 1,
              stroke: "transparent",
              strokeWidth: 0
            },
            labels: baseLabelStyles
          }
        },
        baseProps
      ),
      stack: assign(
        {
          colorScale: colors
        },
        baseProps
      ),
      tooltip: {
        style: assign({}, baseLabelStyles, { padding: 0, pointerEvents: "none" }),
        flyoutStyle: {
          stroke: grey900,
          strokeWidth: 1,
          fill: "#f0f0f0",
          pointerEvents: "none"
        },
        flyoutPadding: 5,
        cornerRadius: 5,
        pointerLength: 10
      },
      voronoi: assign(
        {
          style: {
            data: {
              fill: "transparent",
              stroke: "transparent",
              strokeWidth: 0
            },
            labels: assign({}, baseLabelStyles, {
              padding: 5,
              pointerEvents: "none"
            }),
            flyout: {
              stroke: grey900,
              strokeWidth: 1,
              fill: "#f0f0f0",
              pointerEvents: "none"
            }
          }
        },
        baseProps
      )
    };
    

    The important line her for you is const sansSerif = "'Poppins', sans-serif"; which will be used as fontFamily in const baseLabelStyles further down below. Since you used the VictoryTheme.material in your example I used this theme as a base in my example which can be found here.

    After that you can simply use this custom theme like this:

    <VictoryChart theme={custom} domainPadding={{ x: 20 }}>
    ...
    </VictoryChart>
    

    I created a codesandbox where you can see the complete example. The custom theme gets created in the CustomTheme.js there and is imported/used in the App.js component.

    You can also read more about creating custom themes on the official documentation.