Line chart
Basic line chart
Below is a basic line chart example. In order to get the gradient below the
line, we're using the <AreaChart>
component and a SVG linearGradient
element
with a unique id
.
Code
Also see /react/charts/common#tooltip
CustomTooltip component
import {
AreaChart,
Area,
XAxis,
YAxis,
Tooltip,
ResponsiveContainer,
CartesianGrid,
} from "recharts";
import CustomTooltip from "../CustomToolTip";
const lineColor = "var(--bfc-attn)";
const locale = "en-us";
/** Convert a Date object to month & year string, e.g. "Jan 2023" */
const monthYearFormatter = new Intl.DateTimeFormat(locale, {
month: "short",
year: "numeric",
});
/** Adds a thousand separator to large numbers, e.g. "1,337" */
const numberFormatter = new Intl.NumberFormat(locale);
const data = [
{ date: "2023-01", value: 242725 },
{ date: "2023-02", value: 237632 },
{ date: "2023-03", value: 216610 },
{ date: "2023-04", value: 225223 },
{ date: "2023-05", value: 210043 },
{ date: "2023-06", value: 237577 },
{ date: "2023-07", value: 266538 },
{ date: "2023-08", value: 278820 },
{ date: "2023-09", value: 252026 },
{ date: "2023-10", value: 258326 },
{ date: "2023-11", value: 260691 },
{ date: "2023-12", value: 262566 },
{ date: "2024-01", value: 276147 },
{ date: "2024-02", value: 240784 },
{ date: "2024-03", value: 274169 },
{ date: "2024-04", value: 224765 },
{ date: "2024-05", value: 211219 },
{ date: "2024-06", value: 267703 },
{ date: "2024-07", value: 274453 },
{ date: "2024-08", value: 246658 },
].map((x) => ({
...x,
formattedDate: monthYearFormatter.format(new Date(x.date)),
}));
export default function BasicLineChart() {
return (
<ResponsiveContainer>
<AreaChart
data={data}
margin={{
// adjust margins to fit your data
left: 10,
}}
>
<defs>
<linearGradient id="basic-line-chart-gradient" x2="0" y2="1">
<stop offset="0%" stopColor={lineColor} stopOpacity={0.8} />
<stop offset="100%" stopColor={lineColor} stopOpacity={0} />
</linearGradient>
</defs>
<CartesianGrid
strokeDasharray="5 5"
vertical={false}
stroke="var(--bfc-base-dimmed)"
/>
<XAxis dataKey="formattedDate" hide />
<YAxis
axisLine={false}
tickLine={false}
type="number"
tickFormatter={(value) => numberFormatter.format(value)}
tick={{ fill: "var(--bfc-base-c-2)", fontSize: 14 }}
dx={-8}
/>
<Tooltip content={<CustomTooltip />} />
<Area
type="monotone"
dataKey="value"
stroke={lineColor}
strokeWidth={2}
fill="url(#basic-line-chart-gradient)"
/>
</AreaChart>
</ResponsiveContainer>
);
}
import {
AreaChart,
Area,
XAxis,
YAxis,
Tooltip,
ResponsiveContainer,
CartesianGrid,
} from "recharts";
import CustomTooltip from "../CustomToolTip";
const lineColor = "var(--bfc-attn)";
const locale = "en-us";
/** Convert a Date object to month & year string, e.g. "Jan 2023" */
const monthYearFormatter = new Intl.DateTimeFormat(locale, {
month: "short",
year: "numeric",
});
/** Adds a thousand separator to large numbers, e.g. "1,337" */
const numberFormatter = new Intl.NumberFormat(locale);
const data = [
{ date: "2023-01", value: 242725 },
{ date: "2023-02", value: 237632 },
{ date: "2023-03", value: 216610 },
{ date: "2023-04", value: 225223 },
{ date: "2023-05", value: 210043 },
{ date: "2023-06", value: 237577 },
{ date: "2023-07", value: 266538 },
{ date: "2023-08", value: 278820 },
{ date: "2023-09", value: 252026 },
{ date: "2023-10", value: 258326 },
{ date: "2023-11", value: 260691 },
{ date: "2023-12", value: 262566 },
{ date: "2024-01", value: 276147 },
{ date: "2024-02", value: 240784 },
{ date: "2024-03", value: 274169 },
{ date: "2024-04", value: 224765 },
{ date: "2024-05", value: 211219 },
{ date: "2024-06", value: 267703 },
{ date: "2024-07", value: 274453 },
{ date: "2024-08", value: 246658 },
].map((x) => ({
...x,
formattedDate: monthYearFormatter.format(new Date(x.date)),
}));
export default function BasicLineChart() {
return (
<ResponsiveContainer>
<AreaChart
data={data}
margin={{
// adjust margins to fit your data
left: 10,
}}
>
<defs>
<linearGradient id="basic-line-chart-gradient" x2="0" y2="1">
<stop offset="0%" stopColor={lineColor} stopOpacity={0.8} />
<stop offset="100%" stopColor={lineColor} stopOpacity={0} />
</linearGradient>
</defs>
<CartesianGrid
strokeDasharray="5 5"
vertical={false}
stroke="var(--bfc-base-dimmed)"
/>
<XAxis dataKey="formattedDate" hide />
<YAxis
axisLine={false}
tickLine={false}
type="number"
tickFormatter={(value) => numberFormatter.format(value)}
tick={{ fill: "var(--bfc-base-c-2)", fontSize: 14 }}
dx={-8}
/>
<Tooltip content={<CustomTooltip />} />
<Area
type="monotone"
dataKey="value"
stroke={lineColor}
strokeWidth={2}
fill="url(#basic-line-chart-gradient)"
/>
</AreaChart>
</ResponsiveContainer>
);
}
Two-line chart
Creating a two-line chart (technically two-"area") is as simple as adding
another <AreaChart>
component with corresponding gradients to the chart.
Code
import {
AreaChart,
Area,
XAxis,
YAxis,
Tooltip,
ResponsiveContainer,
CartesianGrid,
Legend,
} from "recharts";
import CustomTooltip from "../CustomToolTip";
const data = [
{ month: "Jan", avgMinTemp: -6.0, avgMaxTemp: -1.0 },
{ month: "Feb", avgMinTemp: -5.0, avgMaxTemp: 0.0 },
{ month: "March", avgMinTemp: -2.0, avgMaxTemp: 5.0 },
{ month: "April", avgMinTemp: 2.0, avgMaxTemp: 10.0 },
{ month: "May", avgMinTemp: 7.0, avgMaxTemp: 16.0 },
{ month: "June", avgMinTemp: 12.0, avgMaxTemp: 20.0 },
{ month: "July", avgMinTemp: 14.0, avgMaxTemp: 23.0 },
{ month: "Aug", avgMinTemp: 13.0, avgMaxTemp: 22.0 },
{ month: "Sept", avgMinTemp: 10.0, avgMaxTemp: 17.0 },
{ month: "Oct", avgMinTemp: 4.0, avgMaxTemp: 10.0 },
{ month: "Nov", avgMinTemp: -1.0, avgMaxTemp: 4.0 },
{ month: "Dec", avgMinTemp: -4.0, avgMaxTemp: 0.0 },
];
export default function TwolineChart() {
return (
<ResponsiveContainer>
<AreaChart
data={data}
margin={{
// adjust margins to fit your data
left: -25,
}}
>
<defs>
<linearGradient id="chillGradient" x2="0" y2="1">
<stop offset="0%" stopColor="var(--bfc-chill)" stopOpacity={0.5} />
<stop offset="100%" stopColor="var(--bfc-chill)" stopOpacity={0} />
</linearGradient>
<linearGradient id="attnGradient" x2="0" y2="1">
<stop offset="0%" stopColor="var(--bfc-attn)" stopOpacity={0.5} />
<stop offset="100%" stopColor="var(--bfc-attn)" stopOpacity={0} />
</linearGradient>
</defs>
<CartesianGrid
strokeDasharray="5 5"
vertical={false}
stroke="var(--bfc-base-dimmed)"
/>
<XAxis
axisLine={false}
tickLine={false}
dataKey="month"
tick={{ fill: "var(--bfc-base-c-2)", fontSize: 14 }}
/>
<YAxis
axisLine={false}
tickLine={false}
tick={{ fill: "var(--bfc-base-c-2)", fontSize: 14 }}
dx={-8}
/>
<Tooltip content={<CustomTooltip />} />
<Area
baseValue="dataMin"
type="monotone"
dataKey="avgMinTemp"
name="Avg. Min Temp"
strokeWidth={2}
stroke="var(--bfc-chill)"
fill="url(#chillGradient)"
/>
<Area
baseValue="dataMin"
type="monotone"
dataKey="avgMaxTemp"
name="Avg. Max Temp"
strokeWidth={2}
stroke="var(--bfc-attn)"
fill="url(#attnGradient)"
/>
<Legend
align="right"
iconType="square"
formatter={(value) => <span className="bfc-base-2"> {value}</span>}
/>
</AreaChart>
</ResponsiveContainer>
);
}
import {
AreaChart,
Area,
XAxis,
YAxis,
Tooltip,
ResponsiveContainer,
CartesianGrid,
Legend,
} from "recharts";
import CustomTooltip from "../CustomToolTip";
const data = [
{ month: "Jan", avgMinTemp: -6.0, avgMaxTemp: -1.0 },
{ month: "Feb", avgMinTemp: -5.0, avgMaxTemp: 0.0 },
{ month: "March", avgMinTemp: -2.0, avgMaxTemp: 5.0 },
{ month: "April", avgMinTemp: 2.0, avgMaxTemp: 10.0 },
{ month: "May", avgMinTemp: 7.0, avgMaxTemp: 16.0 },
{ month: "June", avgMinTemp: 12.0, avgMaxTemp: 20.0 },
{ month: "July", avgMinTemp: 14.0, avgMaxTemp: 23.0 },
{ month: "Aug", avgMinTemp: 13.0, avgMaxTemp: 22.0 },
{ month: "Sept", avgMinTemp: 10.0, avgMaxTemp: 17.0 },
{ month: "Oct", avgMinTemp: 4.0, avgMaxTemp: 10.0 },
{ month: "Nov", avgMinTemp: -1.0, avgMaxTemp: 4.0 },
{ month: "Dec", avgMinTemp: -4.0, avgMaxTemp: 0.0 },
];
export default function TwolineChart() {
return (
<ResponsiveContainer>
<AreaChart
data={data}
margin={{
// adjust margins to fit your data
left: -25,
}}
>
<defs>
<linearGradient id="chillGradient" x2="0" y2="1">
<stop offset="0%" stopColor="var(--bfc-chill)" stopOpacity={0.5} />
<stop offset="100%" stopColor="var(--bfc-chill)" stopOpacity={0} />
</linearGradient>
<linearGradient id="attnGradient" x2="0" y2="1">
<stop offset="0%" stopColor="var(--bfc-attn)" stopOpacity={0.5} />
<stop offset="100%" stopColor="var(--bfc-attn)" stopOpacity={0} />
</linearGradient>
</defs>
<CartesianGrid
strokeDasharray="5 5"
vertical={false}
stroke="var(--bfc-base-dimmed)"
/>
<XAxis
axisLine={false}
tickLine={false}
dataKey="month"
tick={{ fill: "var(--bfc-base-c-2)", fontSize: 14 }}
/>
<YAxis
axisLine={false}
tickLine={false}
tick={{ fill: "var(--bfc-base-c-2)", fontSize: 14 }}
dx={-8}
/>
<Tooltip content={<CustomTooltip />} />
<Area
baseValue="dataMin"
type="monotone"
dataKey="avgMinTemp"
name="Avg. Min Temp"
strokeWidth={2}
stroke="var(--bfc-chill)"
fill="url(#chillGradient)"
/>
<Area
baseValue="dataMin"
type="monotone"
dataKey="avgMaxTemp"
name="Avg. Max Temp"
strokeWidth={2}
stroke="var(--bfc-attn)"
fill="url(#attnGradient)"
/>
<Legend
align="right"
iconType="square"
formatter={(value) => <span className="bfc-base-2"> {value}</span>}
/>
</AreaChart>
</ResponsiveContainer>
);
}
Multi-line chart
Below is a bit more complex line chart example. The chart has a clickable legend which lets you toggle lines on and off, and highlights the corresponding line when hovering.
Code
import { Checkbox, Inline } from "@intility/bifrost-react";
import { useState } from "react";
import {
CartesianGrid,
Legend,
Line,
LineChart,
ResponsiveContainer,
Tooltip,
XAxis,
YAxis,
} from "recharts";
import CustomTooltip from "../CustomToolTip";
const locale = "en-us";
/** Convert a Date object to month & year string, e.g. "Jan 2023" */
const monthYearFormatter = new Intl.DateTimeFormat(locale, {
month: "short",
year: "numeric",
});
const data = [
{ 1: 231, 2: 210, 3: 276, 4: 323, date: "2023-01" },
{ 1: 202, 2: 182, 3: 237, 4: 254, date: "2023-02" },
{ 1: 223, 2: 169, 3: 286, 4: 315, date: "2023-03" },
{ 1: 210, 2: 176, 3: 241, 4: 275, date: "2023-04" },
{ 1: 220, 2: 197, 3: 221, 4: 306, date: "2023-05" },
{ 1: 209, 2: 190, 3: 236, 4: 287, date: "2023-06" },
{ 1: 225, 2: 203, 3: 224, 4: 285, date: "2023-07" },
{ 1: 236, 2: 160, 3: 280, 4: 314, date: "2023-08" },
{ 1: 233, 2: 177, 3: 242, 4: 327, date: "2023-09" },
{ 1: 237, 2: 160, 3: 284, 4: 342, date: "2023-10" },
{ 1: 226, 2: 197, 3: 267, 4: 318, date: "2023-11" },
{ 1: 207, 2: 192, 3: 281, 4: 338, date: "2023-12" },
{ 1: 235, 2: 167, 3: 229, 4: 308, date: "2024-01" },
{ 1: 232, 2: 208, 3: 265, 4: 324, date: "2024-02" },
{ 1: 233, 2: 173, 3: 264, 4: 305, date: "2024-03" },
{ 1: 227, 2: 163, 3: 293, 4: 291, date: "2024-04" },
{ 1: 236, 2: 171, 3: 205, 4: 321, date: "2024-05" },
{ 1: 210, 2: 173, 3: 223, 4: 275, date: "2024-06" },
{ 1: 228, 2: 161, 3: 207, 4: 331, date: "2024-07" },
{ 1: 222, 2: 196, 3: 271, 4: 314, date: "2024-08" },
].map((x) => ({
...x,
formattedDate: monthYearFormatter.format(new Date(x.date)),
}));
const lines = [
{ dataKey: "1", name: "Data 1", className: "bf-theme-green" },
{ dataKey: "2", name: "Data 2", className: "bf-theme-pink" },
{ dataKey: "3", name: "Data 3", className: "bf-theme-purple" },
{ dataKey: "4", name: "Data 4", className: "bf-theme-yellow" },
];
export default function MultiLineChart() {
const [hoveredLine, setHoveredLine] = useState<string>();
const [hiddenLines, setHiddenLines] = useState<string[]>([]);
const toggleLine = (lineName: string) => {
setHiddenLines((prevState) => {
if (prevState.includes(lineName)) {
return prevState.filter((name) => name !== lineName);
} else {
return [...prevState, lineName];
}
});
};
return (
<ResponsiveContainer>
<LineChart data={data} margin={{ left: -20 }}>
<CartesianGrid
strokeDasharray="5 5"
vertical={false}
stroke="var(--bfc-base-dimmed)"
/>
<XAxis
axisLine={false}
tickLine={false}
tick={{ fill: "var(--bfc-base-c-2)", fontSize: 14 }}
dataKey="formattedDate"
dy={8}
/>
<YAxis
axisLine={false}
tickLine={false}
tick={{ fill: "var(--bfc-base-c-2)", fontSize: 14 }}
dx={-8}
/>
<Tooltip content={<CustomTooltip />} />
{lines.map((line) => (
<Line
isAnimationActive={false}
key={line.dataKey}
type="monotone"
dot={false}
strokeWidth={2}
stroke="var(--bfc-theme)"
opacity={hoveredLine && hoveredLine !== line.dataKey ? 0.3 : 1}
hide={hiddenLines.includes(line.dataKey)}
{...line}
/>
))}
<Legend
content={() => (
<Inline style={{ marginTop: 12 }}>
<Inline.Separator />
{lines.map(({ dataKey, name, className }) => (
<Checkbox
key={dataKey}
label={name}
checked={!hiddenLines.includes(dataKey)}
onChange={() => toggleLine(dataKey)}
labelProps={{
onMouseEnter: () => setHoveredLine(dataKey),
onMouseLeave: () => setHoveredLine(undefined),
className: `bfc-base-2 ${className}`,
}}
/>
))}
</Inline>
)}
/>
</LineChart>
</ResponsiveContainer>
);
}
import { Checkbox, Inline } from "@intility/bifrost-react";
import { useState } from "react";
import {
CartesianGrid,
Legend,
Line,
LineChart,
ResponsiveContainer,
Tooltip,
XAxis,
YAxis,
} from "recharts";
import CustomTooltip from "../CustomToolTip";
const locale = "en-us";
/** Convert a Date object to month & year string, e.g. "Jan 2023" */
const monthYearFormatter = new Intl.DateTimeFormat(locale, {
month: "short",
year: "numeric",
});
const data = [
{ 1: 231, 2: 210, 3: 276, 4: 323, date: "2023-01" },
{ 1: 202, 2: 182, 3: 237, 4: 254, date: "2023-02" },
{ 1: 223, 2: 169, 3: 286, 4: 315, date: "2023-03" },
{ 1: 210, 2: 176, 3: 241, 4: 275, date: "2023-04" },
{ 1: 220, 2: 197, 3: 221, 4: 306, date: "2023-05" },
{ 1: 209, 2: 190, 3: 236, 4: 287, date: "2023-06" },
{ 1: 225, 2: 203, 3: 224, 4: 285, date: "2023-07" },
{ 1: 236, 2: 160, 3: 280, 4: 314, date: "2023-08" },
{ 1: 233, 2: 177, 3: 242, 4: 327, date: "2023-09" },
{ 1: 237, 2: 160, 3: 284, 4: 342, date: "2023-10" },
{ 1: 226, 2: 197, 3: 267, 4: 318, date: "2023-11" },
{ 1: 207, 2: 192, 3: 281, 4: 338, date: "2023-12" },
{ 1: 235, 2: 167, 3: 229, 4: 308, date: "2024-01" },
{ 1: 232, 2: 208, 3: 265, 4: 324, date: "2024-02" },
{ 1: 233, 2: 173, 3: 264, 4: 305, date: "2024-03" },
{ 1: 227, 2: 163, 3: 293, 4: 291, date: "2024-04" },
{ 1: 236, 2: 171, 3: 205, 4: 321, date: "2024-05" },
{ 1: 210, 2: 173, 3: 223, 4: 275, date: "2024-06" },
{ 1: 228, 2: 161, 3: 207, 4: 331, date: "2024-07" },
{ 1: 222, 2: 196, 3: 271, 4: 314, date: "2024-08" },
].map((x) => ({
...x,
formattedDate: monthYearFormatter.format(new Date(x.date)),
}));
const lines = [
{ dataKey: "1", name: "Data 1", className: "bf-theme-green" },
{ dataKey: "2", name: "Data 2", className: "bf-theme-pink" },
{ dataKey: "3", name: "Data 3", className: "bf-theme-purple" },
{ dataKey: "4", name: "Data 4", className: "bf-theme-yellow" },
];
export default function MultiLineChart() {
const [hoveredLine, setHoveredLine] = useState<string>();
const [hiddenLines, setHiddenLines] = useState<string[]>([]);
const toggleLine = (lineName: string) => {
setHiddenLines((prevState) => {
if (prevState.includes(lineName)) {
return prevState.filter((name) => name !== lineName);
} else {
return [...prevState, lineName];
}
});
};
return (
<ResponsiveContainer>
<LineChart data={data} margin={{ left: -20 }}>
<CartesianGrid
strokeDasharray="5 5"
vertical={false}
stroke="var(--bfc-base-dimmed)"
/>
<XAxis
axisLine={false}
tickLine={false}
tick={{ fill: "var(--bfc-base-c-2)", fontSize: 14 }}
dataKey="formattedDate"
dy={8}
/>
<YAxis
axisLine={false}
tickLine={false}
tick={{ fill: "var(--bfc-base-c-2)", fontSize: 14 }}
dx={-8}
/>
<Tooltip content={<CustomTooltip />} />
{lines.map((line) => (
<Line
isAnimationActive={false}
key={line.dataKey}
type="monotone"
dot={false}
strokeWidth={2}
stroke="var(--bfc-theme)"
opacity={hoveredLine && hoveredLine !== line.dataKey ? 0.3 : 1}
hide={hiddenLines.includes(line.dataKey)}
{...line}
/>
))}
<Legend
content={() => (
<Inline style={{ marginTop: 12 }}>
<Inline.Separator />
{lines.map(({ dataKey, name, className }) => (
<Checkbox
key={dataKey}
label={name}
checked={!hiddenLines.includes(dataKey)}
onChange={() => toggleLine(dataKey)}
labelProps={{
onMouseEnter: () => setHoveredLine(dataKey),
onMouseLeave: () => setHoveredLine(undefined),
className: `bfc-base-2 ${className}`,
}}
/>
))}
</Inline>
)}
/>
</LineChart>
</ResponsiveContainer>
);
}