MUI (formerly Material UI) is an extensive library of React components styled to match Google’s Material Design. It includes a ton of components, and at the start of November 2023, one more thing was added: Charts. There are a lot of great web and React chart libraries, but MUI X Charts was an easy first choice for my current client because they’ve standardized on MUI org-wide. We’ll keep using MUI X Charts until we have any needs it can’t meet.

MUI X Charts makes it very easy to get a basic chart working with simple components like LineChart and BarChart. If you have more advanced needs, it also provides a chart composition API to let you customize your charts further. Unfortunately, when your needs grow beyond what the simple components can provide, there’s a somewhat steep learning curve (chart jokes!) to get used to the composition API.

To help with the composition API’s learning curve, let’s walk through an example of implementing a chart that goes beyond the basics. First, we’ll look at what we can and can’t accomplish using the basic LineChart component. Then, we’ll see how to switch over to the composition API and re-enable the nice default features of LineChart. Finally, we’ll see how the composition API lets us add additional features, such as reference lines.

Gauging the temperature

To have a chart we can work with post, let’s track the low-temperature data in two cities for a week. Here’s how we could implement that with MUI X Charts’ LineChart component:

import { LineChart } from "@mui/x-charts";
import dayjs from "dayjs";

const xAxisData = [
  new Date("2023-12-04"),
  new Date("2023-12-05"),
  new Date("2023-12-06"),
  new Date("2023-12-07"),
  new Date("2023-12-08"),
  new Date("2023-12-09"),
  new Date("2023-12-10"),
];
const seriesData = [
  [43, 38, 36, 30, 37, 43, 44],
  [31, 28, 27, 27, 33, 40, 35],
];

export default function TemperatureChart() {
  return (
    <div>
      <LineChart
        xAxis={[
          {
            label: "Date",
            data: xAxisData,
            tickInterval: xAxisData,
            scaleType: "time",
            valueFormatter: (date) => dayjs(date).format("MMM D"),
          },
        ]}
        yAxis={[{ label: "Temperature (°F)" }]}
        series={[
          { label: "Atlanta, GA", data: seriesData[0] },
          { label: "Toronto, ON", data: seriesData[1] },
        ]}
        height={400}
      />
    </div>
  );
}

Here’s the line chart it generates for us:

The chart above is a live React component. If you’re on a device with a mouse, try hovering over the chart, and you’ll see a tooltip that provides extra data on the points you’re over.

Say we show this chart to the business, and they love it. They just want one more feature: they want to show a line that indicates the freezing temperature. We check the docs, and we discover that MUI X Charts has the concept of reference lines–we even find a convenient example with reference lines. But when we check that example’s source code to see how it works, it’s confusing. All the other examples so far on the page use the LineChart component, but this one doesn’t use LineChart at all! Instead, it uses several different components, like ChartContainer and LinePlot. And there’s no information on that page about what these components are. It looks like we’re going to have to figure out how to draw the rest of the chart!

It turns out these components are part of Charts’ composition API, which we’ll need to use if we want to add a reference line.

Composing ourselves

Let’s start by replacing our current functionality with the composition API piece by piece. When we’re done, then we can add in the reference line.

First, let’s pull out a few values that won’t change, so that it’s easier to see what’s specific to the components:

 export default function TemperatureChart() {
+  const xAxis = [
+    {
+      label: 'Date',
+      data: xAxisData,
+      tickInterval: xAxisData,
+      scaleType: 'time',
+      valueFormatter: date => dayjs(date).format('MMM D'),
+    },
+  ];
+  const yAxis = [{label: 'Temperature (°F)'}];
+  const height = 400;
+
   return (
     <div>
       <LineChart
-        xAxis={[
-          {
-            label: 'Date',
-            data: xAxisData,
-            tickInterval: xAxisData,
-            scaleType: 'time',
-            valueFormatter: date => dayjs(date).format('MMM D'),
-          },
-        ]}
-        yAxis={[{label: 'Temperature (°F)'}]}
+        xAxis={xAxis}
+        yAxis={yAxis}
         series={[
           {label: 'Atlanta, GA', data: seriesData[0]},
           {label: 'Toronto, ON', data: seriesData[1]},
         ]}
-        height={400}
+        height={height}
       />
     </div>

Next, let’s replace the LineChart with a composition-based chart. We’ll start with the minimum, a line plot:

-import {LineChart} from '@mui/x-charts';
+import {LinePlot, ResponsiveChartContainer} from '@mui/x-charts';
 import dayjs from 'dayjs';
...
       />
-      <LineChart
+      <ResponsiveChartContainer
         xAxis={xAxis}
         yAxis={yAxis}
         series={[
-          {label: 'Atlanta, GA', data: seriesData[0]},
-          {label: 'Toronto, ON', data: seriesData[1]},
+          {type: 'line', label: 'Atlanta, GA', data: seriesData[0]},
+          {type: 'line', label: 'Toronto, ON', data: seriesData[1]},
         ]}
         height={height}
-      />
+      >
+        <LinePlot />
+      </ResponsiveChartContainer>
     </div>

ResponsiveChartContainer is needed to match the automatic width that LineChart allows. If you’re building a chart that has a fixed width, you can use ChartContainer instead.

Note that the lines in the series array changed. LineChart can assume its series are lines—but ResponsiveChartContainer cannot because it supports multiple types of plots. So we have to specify type: 'line' so ResponsiveChartContainer knows what to draw.

When we make this change, we see our two lines but literally nothing else:

With this foundation in place, let’s add back in all the features we had in our original LineChart. I’ll add just a few components at a time so we can understand how each one helps us.

Get back to where we once belonged

At the most basic, we will probably want to add X and Y axes, circles that show each data point, and a legend that describes what the two lines are:

-import {LinePlot, ResponsiveChartContainer} from '@mui/x-charts';
+import {
+  ChartsXAxis,
+  ChartsYAxis,
+  LinePlot,
+  MarkPlot,
+  ResponsiveChartContainer,
+} from '@mui/x-charts';
+import {ChartsLegend} from '@mui/x-charts/ChartsLegend';
 import dayjs from 'dayjs';
...
         height={height}
       >
         <LinePlot />
+        <ChartsXAxis />
+        <ChartsYAxis />
+        <MarkPlot />
+        <ChartsLegend />
       </ResponsiveChartContainer>
     </div>
   );

Note that ChartsLegend is imported from the /ChartsLegend path rather than directly from the package. Interestingly, ChartsLegend is not exported from the root of the package.

Now the chart looks a lot better:

Our original line chart had some interactivity when we hovered over it with the mouse, though. Let’s add a vertical line, highlighting the circles under the line, and a tooltip that shows the X value you have highlighted and the corresponding Y value for each line:

 import {
+  ChartsAxisHighlight,
+  ChartsTooltip,
   ChartsXAxis,
   ChartsYAxis,
+  LineHighlightPlot,
   LinePlot,
...
         <ChartsYAxis />
         <MarkPlot />
         <ChartsLegend />
+        <LineHighlightPlot />
+        <ChartsAxisHighlight x="line" />
+        <ChartsTooltip trigger="axis" />
       </ResponsiveChartContainer>

Note that it’s important that LineHighlightPlot appears after MarkPlot in the JSX: this is what places the circle highlights on top of the circles themselves. If MarkPlot appears second, it will be rendered on top of LineHighlightPlot, and the highlights won’t be visible.

With this, we’ve replicated the functionality we were using from the original LineChart:

Where no chart has gone before

Now we’re ready to add the reference line. The hard part is done; with the composition API groundwork we’ve laid, it’s straightforward:

 import {
   ChartsAxisHighlight,
+  ChartsReferenceLine,
   ChartsTooltip,
...
         <ChartsTooltip trigger="axis" />
+        <ChartsReferenceLine
+          y={32}
+          label="Freezing"
+          labelAlign="end"
+          lineStyle={{stroke: '#128128', strokeDasharray: '3 3'}}
+        />
       </ResponsiveChartContainer>

And now we have our reference line:

Looking at all the code we had to write to replicate the functionality of LineChart, it’s clear how powerful that high-level component is. By comparison, the composition API can feel a bit tedious to use for a case like this, where we need just one additional feature. But the composition API can do a lot more than adding a reference line: for example, it can combine multiple types of plots in one chart, which wouldn’t be possible with LineChart alone. It’s work to learn the composition API, but it can pay off later when you need even more of the advanced features.

The road less traveled

When trying to figure out how to implement the reference line, I had a little trouble navigating my way through the Chart docs. I actually didn’t find the composition API page until after I finished setting up the chart when I was writing this blog post!

After I found the reference line example, I looked for the components I found there in the API Reference section of the docs. Unfortunately, some weren’t listed at all, such as ChartContainer. Others didn’t include any information about what those components are for, such as MarkPlot. If you happen to find the right path through the docs (i.e., the composition API page first), you’ll have the information you need, but if you take a different path, you’ll be left in the dark.

I’ll think about whether there are any suggestions I can propose to the MUI docs that might help me and others discover this more easily in the future. Either way, I hope this walkthrough was helpful to introduce you to MUI X Charts and the composition API!

Josh Justice

Person An icon of a human figure Status
Double Agent
Hash An icon of a hash sign Code Name
Agent 00155
Location An icon of a map marker Location
Conyers, GA