Liniendiagramm für React
Eine oder mehrere Linien über die Zeit, mit runden Achsenwerten, Fadenkreuz, Pfeiltasten und Datentabelle für Screenreader.
Eine von 7 Components in Charts
Verläufe über die Zeit, für alle lesbar
Ein Liniendiagramm zeigt, wie sich ein Wert über die Zeit ändert, und es ist das Diagramm, das Menschen am schnellsten lesen. Dieses zeichnet das Kit selbst in SVG, es wählt runde Achsenwerte von allein, formatiert Daten und Zahlen in der Sprache der Seite und zeigt einen Fadenkreuz-Tooltip mit allen Reihen am gewählten Punkt.
„Eine Reihe“ ist der einfache Verlauf einer Kennzahl wie Besucher oder Umsatz. „Mehrere Reihen“ vergleicht bis zu vier Linien mit Legende, „Dieses und letztes Jahr“ stellt den aktuellen Zeitraum gegen den vorigen als gestrichelte Linie. „Gerade mit Punkten“ zeichnet gerade Strecken mit Punkten, ehrlich bei wenigen Werten wie Quartalszahlen.
Bleib bei höchstens vier Linien, mehr werden zu Spaghetti. Beschrifte den letzten Wert am Ende jeder Linie, wenn Platz ist. Pfeiltasten gehen durch die Punkte, darunter folgt eine Datentabelle für Screenreader, behalte beides, es kostet nichts und hilft allen.
Variants
Details
Was Liniendiagramm mitbringt, aus Registry und Quellcode gelesen.
- Kategorie
- Charts
- Bereich
- Elements
- Variants
- 4
- Version
- 1.0.0 · seit 1.0.0
- Datei
- components/ui/line-chart.tsx
- Bringt mit
- utilskitchart
- npm-Pakete
- Keine
- Sprachen
- Texte kommen über Props
- Studio
- Im Code anpassen
- In Sammlungen
- App-Dashboard
- Mit KI bauen
- Per MCP in Claude, Cursor, Copilot und Codex
Props
Die Schnittstelle LineChartProps aus components/ui/line-chart.tsx. Beschreibungen stammen aus den Kommentaren im Code und sind auf Englisch. Nimmt außerdem alle Props von ChartA11y an.
dataPflichtDatum[]Rows, one per x value, e.g. { month: '2026-01', revenue: 31200 }
xKeyPflichtstringKey of the x value. "2026-03" and "2026-03-14" are read as dates and formatted per language.
seriesPflichtChartSeries[]One line per series, in slot order (accent, grey, two accent tones)
formatoptionalIntl.NumberFormatOptionsValue format for tooltip, table and axis, e.g. { style: 'currency', currency: 'EUR', maximumFractionDigits: 0 }
xFormatoptionalIntl.DateTimeFormatOptionsAxis format for dates, e.g. { month: 'short' }
tooltipXFormatoptionalIntl.DateTimeFormatOptionsTooltip format for dates, e.g. { month: 'long', year: 'numeric' }
curveoptional'smooth' | 'linear'smooth (monotone, never overshoots) or straight segments
dotsoptionalboolean | 'end'Dots on every point, only at the end, or none
endLabelsoptionalbooleanValue at the end of each line, next to the last point
baselineoptional'zero' | 'auto'zero: the axis starts at 0. auto: it hugs the data (good for small changes).
heightoptionalnumberPlot height in px, axis labels included
legendoptionalbooleanLegend above the plot. Default: for two series and more.
xLabeloptionalstringHeader of the first table column, e.g. "Month"
Barrierefreiheit
Aus dem Quellcode der Component abgeleitet.
- Kommt ohne Client-State aus. Der Server schreibt sie vollständig, sie ist auch ohne JavaScript lesbar.
Bewegung
- Ohne Animationen, sie steht sofort ruhig da.
Quellcode
Die Datei, die aiw add in dein Projekt legt. Sie gehört danach dir.
Datei anzeigen, 141 Zeilen
'use client';
import { round } from '@/lib/kit';
import { CartesianFrame, GlideDot, linePath, seriesOpacity, numberFormatter, seriesColor, textWidth, useIntlLocale, type ChartA11y, type ChartSeries, type Datum } from '@/components/ui/chart';
export interface LineChartProps extends ChartA11y {
/** Rows, one per x value, e.g. { month: '2026-01', revenue: 31200 } */
data: Datum[];
/** Key of the x value. "2026-03" and "2026-03-14" are read as dates and formatted per language. */
xKey: string;
/** One line per series, in slot order (accent, grey, two accent tones) */
series: ChartSeries[];
/** Value format for tooltip, table and axis, e.g. { style: 'currency', currency: 'EUR', maximumFractionDigits: 0 } */
format?: Intl.NumberFormatOptions;
/** Axis format for dates, e.g. { month: 'short' } */
xFormat?: Intl.DateTimeFormatOptions;
/** Tooltip format for dates, e.g. { month: 'long', year: 'numeric' } */
tooltipXFormat?: Intl.DateTimeFormatOptions;
/** smooth (monotone, never overshoots) or straight segments */
curve?: 'smooth' | 'linear';
/** Dots on every point, only at the end, or none */
dots?: boolean | 'end';
/** Value at the end of each line, next to the last point */
endLabels?: boolean;
/** zero: the axis starts at 0. auto: it hugs the data (good for small changes). */
baseline?: 'zero' | 'auto';
/** Plot height in px, axis labels included */
height?: number;
/** Legend above the plot. Default: for two series and more. */
legend?: boolean;
/** Header of the first table column, e.g. "Month" */
xLabel?: string;
className?: string;
}
/**
* Lines over time or categories, one or several series.
* <LineChart data={rows} xKey="month" series={[{ key: 'revenue', label: 'Revenue' }]} format={{ style: 'currency', currency: 'EUR' }} />
*/
export function LineChart({ data, xKey, series, format, xFormat, tooltipXFormat, curve = 'smooth', dots = 'end', endLabels, baseline = 'zero', height = 260, legend, xLabel, label, summary, labels, animate, className }: LineChartProps) {
const intl = useIntlLocale();
const fmt = numberFormatter(intl, format);
const nums = data.flatMap((d) => series.map((s) => d[s.key])).filter((v): v is number => typeof v === 'number');
const lo = nums.length ? Math.min(...nums) : 0;
const hi = nums.length ? Math.max(...nums) : 1;
const extent: [number, number] = baseline === 'zero' ? [Math.min(0, lo), Math.max(0, hi)] : [lo - (hi - lo) * 0.08, hi + (hi - lo) * 0.08];
const last = (key: string) => {
for (let i = data.length - 1; i >= 0; i--) if (typeof data[i][key] === 'number') return { i, v: data[i][key] as number };
return null;
};
const labelW = endLabels ? Math.max(...series.map((s) => textWidth(fmt(last(s.key)?.v ?? 0), 12))) + 14 : 0;
// On narrow charts the end labels would squeeze the plot, the card header carries the value there
const padRight = (w: number) => (w >= 480 ? labelW : 0);
return (
<CartesianFrame
data={data}
xKey={xKey}
series={series}
format={format}
xFormat={xFormat}
tooltipXFormat={tooltipXFormat}
height={height}
scale="point"
extent={extent}
legend={legend}
legendShape="line"
padRight={padRight}
crosshair
xLabel={xLabel}
label={label}
summary={summary}
labels={labels}
animate={animate}
className={className}
marks={({ cat, val, animated, width, focus }) =>
series.map((s, si) => {
const color = seriesColor(si, s.color);
// Gaps (null) split the line into segments
const segments: [number, number][][] = [[]];
data.forEach((d, i) => {
const v = d[s.key];
if (typeof v === 'number') segments[segments.length - 1].push([cat(i), val(v)]);
else if (segments[segments.length - 1].length) segments.push([]);
});
const end = last(s.key);
return (
<g key={s.key} className="kitch-series" opacity={seriesOpacity(si, focus)}>
{segments
.filter((seg) => seg.length)
.map((seg, k) => (
<path
key={k}
d={linePath(seg, curve === 'smooth')}
fill="none"
stroke={color}
strokeWidth="2"
strokeLinecap="round"
strokeLinejoin="round"
strokeDasharray={s.dashed ? '4 5' : undefined}
pathLength={s.dashed ? undefined : 1}
className={animated ? (s.dashed ? 'kitch-clip' : 'kitch-draw') : undefined}
style={animated ? ({ '--kitch-d': `${si * 120}ms` } as React.CSSProperties) : undefined}
/>
))}
{dots === true &&
data.map((d, i) =>
typeof d[s.key] === 'number' ? (
<circle
key={i}
cx={cat(i)}
cy={val(d[s.key] as number)}
r="4"
fill={color}
stroke="var(--kitch-surface)"
strokeWidth="2"
className={animated ? 'kitch-pop' : undefined}
style={animated ? ({ '--kitch-d': `${round(120 + si * 120 + (i / Math.max(1, data.length - 1)) * 680, 0)}ms` } as React.CSSProperties) : undefined}
/>
) : null,
)}
{dots === 'end' && end && <circle cx={cat(end.i)} cy={val(end.v)} r="4" fill={color} stroke="var(--kitch-surface)" strokeWidth="2" className={animated ? 'kitch-pop' : undefined} style={animated ? ({ '--kitch-d': `${780 + si * 120}ms` } as React.CSSProperties) : undefined} />}
{endLabels && end && width >= 480 && (
<text x={cat(end.i) + 10} y={val(end.v)} dy="0.34em" className={`text-[12px] font-medium tabular-nums ${animated ? 'kitch-fade' : ''}`} fill="var(--foreground)" style={animated ? ({ '--kitch-d': `${860 + si * 120}ms` } as React.CSSProperties) : undefined}>
{fmt(end.v)}
</text>
)}
</g>
);
})
}
overlay={({ cat, val, shown, open, jump, focus }) =>
shown !== null &&
series.map((s, si) => {
const v = data[shown]?.[s.key];
return typeof v === 'number' ? <GlideDot key={s.key} x={cat(shown)} y={val(v)} open={open} jump={jump} color={seriesColor(si, s.color)} opacity={seriesOpacity(si, focus)} /> : null;
})
}
/>
);
}Häufige Fragen
Wie installiere ich Liniendiagramm?
Mit aiw add @ai-website/line-chart. Der Befehl legt die Datei components/ui/line-chart.tsx in dein Projekt, der Code gehört danach dir. Mit dabei sind chart. Weitere npm-Pakete braucht sie nicht.
Welche Varianten hat Liniendiagramm?
4 Varianten, Eine Reihe, Mehrere Reihen, Dieses und letztes Jahr und Gerade mit Punkten.
Kann ein KI-Assistent Liniendiagramm einbauen?
Ja. Über den MCP-Server der aiw CLI finden Claude, Cursor, GitHub Copilot, Windsurf, Codex und Gemini Liniendiagramm mit aiw_components und installieren sie mit aiw_add @ai-website/line-chart.