Render uPlot charts in the terminal with React Ink. Reuse your existing browser uPlot config — series, axes, scales — and get pixel-accurate, truecolor terminal charts. Auto-detects the best output for your terminal: Unicode block art, or native kitty / sixel / iTerm2 graphics protocols for real inline images.
A terminal charting / TUI dataviz library for Node.js — line charts, time series, live-updating dashboards, and trading-style plots, straight in your CLI.
- Real uPlot config, not a new API — paste the same
opts/datayou'd use in the browser. Series, scales, dual axes, custom tick formatters all work. - Truecolor & graphics protocols — beyond ASCII/Unicode: emit real images via the kitty, sixel, and iTerm2 inline-image protocols, auto-detected per terminal.
- Built for live data — update the
dataprop to animate; designed for streaming, real-time dashboards. - Composable — it's a normal Ink
<Box>/<Text>component; embed it in any TUI layout. - TypeScript-first, ESM, fully typed.
npm install ink-uplot uplot ink reactuplot, ink, and react are peer dependencies. canvas (node-canvas) is pulled in automatically but needs system libraries (Cairo, Pango):
- macOS:
brew install pkg-config cairo pango - Debian/Ubuntu:
sudo apt-get install build-essential libcairo2-dev libpango1.0-dev - See node-canvas compiling for other platforms.
import React from 'react';
import { render } from 'ink';
import { InkUPlot } from 'ink-uplot';
const opts = {
series: [
{},
{ stroke: 'cyan', label: 'Price', width: 2 },
],
axes: [
{ stroke: '#555', grid: { stroke: '#333' } },
{ stroke: '#555', grid: { stroke: '#333' } },
],
};
const data = [
[1, 2, 3, 4, 5, 6, 7, 8, 9, 10], // x values
[10, 25, 15, 30, 20, 35, 25, 40, 30, 45], // y values
];
function App() {
return <InkUPlot opts={opts} data={data} width={80} height={24} />;
}
render(<App />);ink-uplot picks the highest-fidelity output your terminal supports and falls back gracefully. Detection is automatic (detectFormat()), or force one with the format prop.
| Format | What it is | Terminals |
|---|---|---|
symbols |
Truecolor Unicode block art (via chafa) | Any terminal — universal fallback |
kitty |
kitty graphics protocol | kitty, Ghostty, Konsole |
sixels |
Sixel graphics | foot, Windows Terminal, xterm (+sixel) |
iterm2 |
iTerm2 inline images | iTerm2, WezTerm, VS Code |
// Force a format instead of auto-detecting:
<InkUPlot opts={opts} data={data} format="symbols" />| Prop | Type | Default | Description |
|---|---|---|---|
opts |
uPlot.Options |
required | Standard uPlot options. Interactive options (cursor, select, legend) are stripped automatically. |
data |
uPlot.AlignedData |
required | uPlot data array — identical to browser uPlot. |
width |
number |
stdout.columns or 80 |
Chart width in terminal columns. |
height |
number |
24 |
Chart height in terminal rows. |
format |
'symbols' | 'kitty' | 'sixels' | 'iterm2' |
auto-detected | Output format for the current terminal. |
showAxes |
boolean |
true |
Render text axes around the chart. false for a borderless chart. |
color |
boolean |
true |
Enable truecolor ANSI output. |
- Truecolor rendering — 24-bit color Unicode block art, or real inline images via graphics protocols.
- Text axes — Y-axis labels (left and/or right) and X-axis ticks render as real, copy-pasteable text.
- Dual Y-axis — put scales on the left, right, or both via uPlot's
scaleandsideconfig. - Timestamp auto-detection — unix-timestamp X values are formatted as dates automatically.
- Custom axis formatters — set
valueson an axis to control tick labels (e.g.mm:ss). - Live / streaming data — update the
dataprop to animate; resize-aware. - Responsive — follows terminal resize when
widthisn't fixed.
Run any example with npx tsx examples/<name>.tsx. Press q to quit.
| Example | Description |
|---|---|
basic-line.tsx |
Simple sine wave with axes |
multi-series.tsx |
Three overlapping series |
dual-y-axis.tsx |
Price (left) + Volume (right) Y-axis |
shaded-area.tsx |
Area charts with translucent fill |
timestamps.tsx |
90-day time series with date X-axis |
live-trading.tsx |
Streaming data at 100ms, mm:ss X-axis, highlighted last value |
no-axes.tsx |
Minimal borderless chart (showAxes={false}) |
line-width-test.tsx |
Interactive line-width comparison (arrow keys) |
- A minimal DOM shim provides fake
document/windowglobals so uPlot can initialize in Node.js. - uPlot renders your series onto a node-canvas instance.
- The canvas pixel buffer is extracted (
getImageData, or a native PNG for iTerm2). - For
symbols/kitty/sixels, chafa-wasm converts pixels to truecolor terminal output; foriterm2the PNG is sent inline. - Text axes are computed separately (nice-numbers tick algorithm) and rendered as Ink
<Text>. - Everything is composed via Ink
<Box>/<Text>layout.
For custom pipelines, the internals are exported:
import {
renderToImageData,
renderToPNG,
pixelsToTerminal,
detectFormat,
} from 'ink-uplot';
// uPlot → pixel buffer
const imageData = await renderToImageData(opts, data, 640, 384);
// pixels → terminal output (symbols / kitty / sixels)
const ansi = await pixelsToTerminal(imageData, { width: 80, height: 24, colors: 'truecolor' });
console.log(ansi);
// uPlot → PNG buffer (used by the iTerm2 fast path)
const png = await renderToPNG(opts, data, 640, 384);- Series colors: high-contrast colors (
'cyan','#ff0000','#00ff88') read best. - Embedding: the component occupies exactly its
width×heightin cells — wrap it in an Ink<Box>to place it in a larger layout. - Shaded areas: use
fillwith alpha ≥ 0.3 (e.g.'rgba(0, 200, 255, 0.4)'); very low alpha may not be visible. - Formats: if a terminal misdetects, pass
formatexplicitly.symbolsworks everywhere.
- glassnode-terminal — an interactive terminal UI for exploring Glassnode on-chain & market crypto data: a three-pane explorer with live charts and price tickers.
Using ink-uplot in your project? Open a PR to add it here.
- Node.js >= 18
- System libraries for node-canvas (Cairo, Pango)


