Zum Inhalt springen
UI Kit
Accent

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

4 Variants

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

Eine Reihe

Eine weiche Linie im Akzent, der letzte Punkt markiert und beschriftet.

Mehrere Reihen

Drei Quellen in Akzent, Grau und hellem Akzentton, mit Legende und einem Tooltip für alle.

Dieses und letztes Jahr

Das laufende Jahr im Akzent, das Vorjahr als ruhige, gestrichelte graue Linie.

Gerade mit Punkten

Gerade Abschnitte und ein Punkt je Monat, für Zählwerte, bei denen jeder Punkt zählt.

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

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.

  • dataPflicht
    Datum[]

    Rows, one per x value, e.g. { month: '2026-01', revenue: 31200 }

  • xKeyPflicht
    string

    Key of the x value. "2026-03" and "2026-03-14" are read as dates and formatted per language.

  • seriesPflicht
    ChartSeries[]

    One line per series, in slot order (accent, grey, two accent tones)

  • formatoptional
    Intl.NumberFormatOptions

    Value format for tooltip, table and axis, e.g. { style: 'currency', currency: 'EUR', maximumFractionDigits: 0 }

  • xFormatoptional
    Intl.DateTimeFormatOptions

    Axis format for dates, e.g. { month: 'short' }

  • tooltipXFormatoptional
    Intl.DateTimeFormatOptions

    Tooltip format for dates, e.g. { month: 'long', year: 'numeric' }

  • curveoptional
    'smooth' | 'linear'

    smooth (monotone, never overshoots) or straight segments

  • dotsoptional
    boolean | 'end'

    Dots on every point, only at the end, or none

  • endLabelsoptional
    boolean

    Value 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).

  • heightoptional
    number

    Plot height in px, axis labels included

  • legendoptional
    boolean

    Legend above the plot. Default: for two series and more.

  • xLabeloptional
    string

    Header 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
components/ui/line-chart.tsx
'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.

Oft gesucht

Direkt zu

In die Zwischenablage kopiert