component — charts
Line Chart
A config-driven line chart on the quebi Chart wrapper, rendering one ink series per config key with styled axes, grid, tooltip, and an interactive legend. Requires the recharts package.
- chart
- line
- recharts
- data
- graph
- visualization
09 examples
Default
A config-driven line chart with two ink series, an interactive legend, and a tooltip.
Single series
One ink line driven entirely by the config.
No grid lines
Hide the cartesian grid for a cleaner surface.
Edge labels only
Show only the first and last X-axis labels for dense series.
Custom series via children
Pass children to take full manual control of the rendered lines while keeping the styled wrapper.
Tooltip formatters
`tooltipProps` reaches the tooltip's own `formatter` and `labelFormatter`. Both return strings or nodes, so the number goes through `formatNumber(value, locale)` — a bare `toLocaleString()` here formats against whatever locale the runtime has, which differs between the prerender and the browser.
A tooltip surface of your own
Pass an element to `tooltip` and it replaces `ChartTooltipContent` entirely — recharts clones it with the active payload.
Reference line, area and dot
`overlays` renders extra recharts children *next to* the series the config generates. Passing them as `children` would replace those series instead.
Axis labels and tick formatting
An axis label on each side, and a `valueFormatter` on the Y ticks. The formatter returns a string, which is why it calls `formatNumber(value, locale)` rather than a component.
source
line-chart.tsxCopy this into your project. Resolve its dependencies from the registryDependencies in the component's API entry.
"use client"
import { useMemo } from "react"
import { Line, LineChart as LineChartPrimitive, type LineProps } from "recharts"
import type { NameType, ValueType } from "recharts/types/component/DefaultTooltipContent"
import {
type BaseChartProps,
CartesianGrid,
Chart,
ChartLegend,
ChartLegendContent,
ChartTooltip,
ChartTooltipContent,
constructCategoryColors,
DEFAULT_COLORS,
getColorValue,
valueToPercent,
XAxis,
YAxis,
} from "@/components/chart"
/**
* LineChart — quebi design system
*
* A config-driven line chart built on the quebi `Chart` wrapper. It renders one
* `<Line>` per `config` key with a styled grid, axes, tooltip, and an
* interactive legend. Series colors come from the quebi palette and can be
* overridden per key via the `config` prop. Pass `children` to take full manual
* control of the rendered series.
*
* Requires the `recharts` npm package as a peer dependency.
*/
export interface LineChartProps<TValue extends ValueType, TName extends NameType>
extends BaseChartProps<TValue, TName> {
connectNulls?: boolean
lineProps?: LineProps
chartProps?: Omit<React.ComponentProps<typeof LineChartPrimitive>, "data" | "stackOffset">
}
export function LineChart<TValue extends ValueType, TName extends NameType>({
data = [],
dataKey,
colors = DEFAULT_COLORS,
connectNulls = false,
type = "default",
config,
children,
// Components
tooltip = true,
tooltipProps,
legend = true,
legendProps,
intervalType = "equidistantPreserveStart",
valueFormatter = (value: number) => value.toString(),
// XAxis
displayEdgeLabelsOnly = false,
xAxisProps,
hideXAxis = false,
// YAxis
yAxisProps,
hideYAxis = false,
hideGridLines = false,
chartProps,
lineProps,
overlays,
...props
}: LineChartProps<TValue, TName>) {
const configKeys = useMemo(() => Object.keys(config), [config])
const categoryColors = useMemo(
() => constructCategoryColors(configKeys, colors),
[configKeys, colors],
)
const configEntries = useMemo(() => Object.entries(config), [config])
return (
<Chart config={config} data={data} dataKey={dataKey} {...props}>
{({ onLegendSelect, selectedLegend }) => (
<LineChartPrimitive
onClick={() => {
onLegendSelect(null)
}}
data={data}
margin={{
bottom: 0,
left: 0,
right: 0,
top: 5,
}}
stackOffset={type === "percent" ? "expand" : undefined}
{...chartProps}
>
{!hideGridLines && <CartesianGrid strokeDasharray="4 4" />}
<XAxis
hide={hideXAxis}
displayEdgeLabelsOnly={displayEdgeLabelsOnly}
intervalType={intervalType}
{...xAxisProps}
/>
<YAxis
hide={hideYAxis}
tickFormatter={type === "percent" ? valueToPercent : valueFormatter}
{...yAxisProps}
/>
{legend && (
<ChartLegend
content={typeof legend === "boolean" ? <ChartLegendContent /> : legend}
{...legendProps}
/>
)}
{tooltip && (
<ChartTooltip
content={
typeof tooltip === "boolean" ? <ChartTooltipContent accessibilityLayer /> : tooltip
}
{...tooltipProps}
/>
)}
{!children
? configEntries.map(([category, values]) => {
const strokeOpacity = selectedLegend && selectedLegend !== category ? 0.1 : 1
const color = getColorValue(values.color || categoryColors.get(category))
return (
<Line
key={category}
dot={false}
name={category}
type="linear"
dataKey={category}
stroke={color}
style={
{
strokeOpacity,
strokeWidth: 2,
"--line-color": color,
} as React.CSSProperties
}
strokeLinejoin="round"
strokeLinecap="round"
connectNulls={connectNulls}
{...lineProps}
/>
)
})
: children}
{overlays}
</LineChartPrimitive>
)}
</Chart>
)
}