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!