Skip to main content
The Line Chart widget renders multi-series line graphs with automatic scaling, legends, and smooth sub-cell rendering.

Basic Usage

import { ui } from "@rezi-ui/core";

ui.lineChart({
  id: "sales-chart",
  width: 60,
  height: 20,
  series: [
    {
      data: [10, 25, 15, 40, 30, 45, 35],
      color: "#3b82f6",
      label: "Revenue",
    },
  ],
});

Props

id
string
Widget identifier for debugging.
width
number
required
Chart width in terminal columns.
height
number
required
Chart height in terminal rows.
series
LineChartSeries[]
required
Data series to plot. Each series has:
  • data: number[] - Y-axis values
  • color: string - Line color (hex or theme)
  • label?: string - Series name for legend
axes
{ x?: ChartAxis, y?: ChartAxis }
Axis configuration:
  • label?: string - Axis label
  • min?: number - Minimum value (auto if omitted)
  • max?: number - Maximum value (auto if omitted)
blitter
GraphicsBlitter
default:"braille"
Rendering mode. One of "braille", "sextant", "quadrant", "halfblock". Note: "ascii" is not supported for line charts.
showLegend
boolean
default:"true"
Show series legend below the chart.

Data Format

Each series is an object with data, color, and optional label:
type LineChartSeries = {
  data: readonly number[];    // Y-axis values
  color: string;              // Hex or theme color
  label?: string;             // Series name
};

Single Series

ui.lineChart({
  id: "temperature",
  width: 50,
  height: 15,
  series: [
    {
      data: [20, 22, 21, 24, 26, 25, 23],
      color: "#ef4444",
      label: "Temperature (°C)",
    },
  ],
});

Multiple Series

ui.lineChart({
  id: "comparison",
  width: 60,
  height: 20,
  series: [
    {
      data: [10, 15, 12, 18, 16, 20, 19],
      color: "#3b82f6",
      label: "Product A",
    },
    {
      data: [8, 12, 14, 11, 15, 17, 16],
      color: "#10b981",
      label: "Product B",
    },
    {
      data: [5, 7, 9, 8, 10, 12, 14],
      color: "#f59e0b",
      label: "Product C",
    },
  ],
  showLegend: true,
});

Axis Configuration

Auto-Scaling (Default)

By default, axes auto-scale to fit data:
ui.lineChart({
  width: 50,
  height: 20,
  series: [{ data: [10, 50, 30, 80], color: "#3b82f6" }],
  // Y-axis: min=10, max=80 (computed)
});

Fixed Y-Axis Range

ui.lineChart({
  width: 50,
  height: 20,
  series: [{ data: [10, 50, 30, 80], color: "#3b82f6" }],
  axes: {
    y: {
      min: 0,
      max: 100,
      label: "Usage (%)",
    },
  },
});

Axis Labels

ui.lineChart({
  width: 60,
  height: 20,
  series: [{ data: values, color: "#3b82f6" }],
  axes: {
    x: { label: "Time (hours)" },
    y: { label: "Requests/sec" },
  },
});

Blitter Modes

Braille (Default)

Smooth curves with 2×4 sub-cell resolution:
ui.lineChart({
  width: 60,
  height: 20,
  series: [{ data: values, color: "#3b82f6" }],
  blitter: "braille", // Default
});

Sextant

Block-based rendering with 2×3 resolution:
ui.lineChart({
  width: 60,
  height: 20,
  series: [{ data: values, color: "#3b82f6" }],
  blitter: "sextant",
});

Quadrant

Pixelated style with 2×2 resolution:
ui.lineChart({
  width: 60,
  height: 20,
  series: [{ data: values, color: "#3b82f6" }],
  blitter: "quadrant",
});

Halfblock

Horizontal bars with 1×2 resolution:
ui.lineChart({
  width: 60,
  height: 20,
  series: [{ data: values, color: "#3b82f6" }],
  blitter: "halfblock",
});

Examples

Real-Time Metrics

import { defineWidget } from "@rezi-ui/core";

type MetricsState = {
  cpuHistory: number[];
  memHistory: number[];
};

function metricsChart(state: MetricsState): VNode {
  return ui.lineChart({
    id: "metrics",
    width: 70,
    height: 25,
    series: [
      {
        data: state.cpuHistory,
        color: "#3b82f6",
        label: "CPU %",
      },
      {
        data: state.memHistory,
        color: "#10b981",
        label: "Memory %",
      },
    ],
    axes: {
      y: { min: 0, max: 100, label: "Usage %" },
      x: { label: "Time" },
    },
    showLegend: true,
  });
}

Financial Data

function stockChart(prices: number[]): VNode {
  return ui.column({ gap: 1 }, [
    ui.text("Stock Price (Last 30 Days)", { variant: "heading" }),
    ui.lineChart({
      id: "stock",
      width: 80,
      height: 30,
      series: [
        {
          data: prices,
          color: prices[prices.length - 1] > prices[0] ? "#10b981" : "#ef4444",
          label: "Price (USD)",
        },
      ],
      axes: {
        y: { label: "Price" },
        x: { label: "Days" },
      },
    }),
  ]);
}

Multi-Metric Dashboard

function dashboard(data: {
  requests: number[];
  errors: number[];
  latency: number[];
}): VNode {
  return ui.column({ gap: 2, p: 1 }, [
    ui.text("API Metrics", { variant: "heading" }),
    
    // Requests chart
    ui.column({ gap: 0 }, [
      ui.text("Request Volume", { style: { bold: true } }),
      ui.lineChart({
        id: "requests",
        width: 70,
        height: 15,
        series: [
          { data: data.requests, color: "#3b82f6", label: "Requests/min" },
        ],
        axes: { y: { min: 0 } },
      }),
    ]),
    
    // Errors chart
    ui.column({ gap: 0 }, [
      ui.text("Error Rate", { style: { bold: true } }),
      ui.lineChart({
        id: "errors",
        width: 70,
        height: 15,
        series: [
          { data: data.errors, color: "#ef4444", label: "Errors/min" },
        ],
        axes: { y: { min: 0 } },
      }),
    ]),
    
    // Latency chart
    ui.column({ gap: 0 }, [
      ui.text("Latency (p95)", { style: { bold: true } }),
      ui.lineChart({
        id: "latency",
        width: 70,
        height: 15,
        series: [
          { data: data.latency, color: "#f59e0b", label: "ms" },
        ],
        axes: { y: { min: 0 } },
      }),
    ]),
  ]);
}

Comparison Chart with Legend

function comparisonChart(
  actual: number[],
  target: number[],
  forecast: number[]
): VNode {
  return ui.lineChart({
    id: "comparison",
    width: 80,
    height: 30,
    series: [
      { data: actual, color: "#3b82f6", label: "Actual" },
      { data: target, color: "#10b981", label: "Target" },
      { data: forecast, color: "#f59e0b", label: "Forecast" },
    ],
    axes: {
      y: { label: "Revenue ($M)" },
      x: { label: "Quarter" },
    },
    showLegend: true,
  });
}

Sparse Data Handling

function sparseChart(data: Array<number | null>): VNode {
  // Filter out nulls and map to indices
  const filtered = data
    .map((val, i) => ({ val, i }))
    .filter(({ val }) => val !== null);
  
  return ui.lineChart({
    id: "sparse",
    width: 60,
    height: 20,
    series: [
      {
        data: filtered.map(({ val }) => val!),
        color: "#3b82f6",
        label: "Valid Points",
      },
    ],
  });
}

Performance

Data Point Limits

  • Braille: Smooth up to ~500 points
  • Sextant: Smooth up to ~400 points
  • Quadrant: Smooth up to ~300 points
  • Halfblock: Smooth up to ~200 points
For larger datasets, consider:
  • Downsampling (e.g., show every Nth point)
  • Rolling windows (show last N points)
  • Aggregation (hourly → daily)

Downsampling Example

function downsample(data: number[], targetPoints: number): number[] {
  if (data.length <= targetPoints) return data;
  
  const step = Math.floor(data.length / targetPoints);
  return data.filter((_, i) => i % step === 0);
}

const largeDataset = Array.from({ length: 10000 }, (_, i) => Math.sin(i / 100));
const downsampled = downsample(largeDataset, 200);

ui.lineChart({
  id: "downsampled",
  width: 60,
  height: 20,
  series: [{ data: downsampled, color: "#3b82f6" }],
});

See Also

Build docs developers (and LLMs) love