# React Sparkline: Table Cells, KPI Tiles, Delta Chips

> Add a React sparkline to table cells and KPI tiles with one scale across rows, a delta chip that knows good from bad, keyboard scrubbing and touch support.

- Author: [Serban Rusu](https://wingo-ui.com/blog/authors/serban), Founder of Wingo UI
- Published: Oct 19, 2026
- Category: [Component guides](https://wingo-ui.com/blog/category/components)
- Reading time: 9 min
- Canonical: https://wingo-ui.com/blog/react-sparkline

## TL;DR

A React sparkline is a word-sized chart with no axes that shows the shape of a trend next to a number, in a table cell, a KPI tile or a sentence. Give every row in a table the same min and max so the lines compare, pair the line with the current value and a delta chip that turns green or red by meaning, and give it an accessible summary such as "from $8,200 to $12,480, +52.2%". The Wingo UI Sparkline, Stat Card and Trend Badge do this from a data array.

You have a table of clients, products or servers, and each row needs to say "going up" or "going down" without turning into a full chart. A React sparkline does that: a word-sized line next to the number, with no axes, no legend and no grid. The hard parts show up later. Each row scales to its own range, so a flat client looks as busy as a growing one. The delta next to it turns red when costs drop. A screen reader hears nothing, and on a phone the tooltip hides under the finger. This guide covers when to use a sparkline, how to put one in a table cell and in a KPI tile, the delta chip that goes with it, and the pitfalls.

## What is a sparkline and when should I use one?

Use a sparkline when the shape of a trend matters more than its exact values. Edward Tufte, who coined the term, defines it as "a small intense, simple, word-sized graphic with typographic resolution" in his [notes on sparkline theory and practice](https://www.edwardtufte.com/notebook/sparkline-theory-and-practice-edward-tufte/), and lists sentences, tables, headlines and spreadsheets as places it can live.

In a React app that means three places:

1. **Table cells and list rows**: revenue per client, response time per endpoint, stock per warehouse.
2. **KPI tiles** on a dashboard, under or beside the big number.
3. **Running text**: "Signups rose all quarter" with the line inline at the size of the text.

A sparkline is the wrong tool when the reader needs to read values off an axis, compare two series in one box, or see dates. Use a [Line chart](https://wingo-ui.com/components/line-chart) for that, and keep the sparkline as the summary that links to it.

## Which React sparkline chart libraries are there?

Pick by the stack you already have: react-sparklines for a plain SVG line, the MUI X SparkLineChart if you use MUI, the Wingo UI Sparkline for table cells with a value and a delta. Facts below are from each project's docs, its source and the npm registry, as of October 19, 2026.

| Option | What it is | Types | Inspect points | Notes |
|---|---|---|---|---|
| [react-sparklines](https://github.com/borisyankov/react-sparklines) | MIT npm package with composable children (`SparklinesLine`, `SparklinesCurve`, `SparklinesBars`, `SparklinesSpots`, `SparklinesReferenceLine`, `SparklinesNormalBand`) | Line, curve, bars, spots | No tooltip; an `onMouseMove` callback on line points and bars | The latest version on npm, 1.7.0, was published in July 2017 |
| [MUI X SparkLineChart](https://mui.com/x/react-charts/sparkline/) | Part of the MIT `@mui/x-charts` package (9.15.0) | Line, or bars with `plotType="bar"`, optional `area` | `showTooltip` and `showHighlight` | Themed with MUI; `yAxis` takes `min` and `max` |
| [Wingo UI Sparkline](https://wingo-ui.com/components/sparkline) | Pro source component, `npx wingo-ui@latest add sparkline` | Line, area, bar, win-loss | `interactive`: hover, touch scrub, arrow keys | Ships with a value-and-delta wrapper, trend colors and an accessible summary |

If your app already uses MUI, the SparkLineChart is the natural pick: it shares your theme, and you avoid adding a second chart stack. react-sparklines is free and depends only on `prop-types`, but it has not had a release since 2017, so any tooltip, keyboard or screen reader support is yours to build. The rest of this guide uses the Wingo UI components.

## How do I add a React sparkline to a component?

Install it and pass an array of numbers, oldest first. The defaults are a 96 by 28 px line with a dot on the last point, which reads well in a phone row.

```bash
npx wingo-ui@latest add sparkline
```

```tsx
import { Sparkline } from "@/components/ui/sparkline";

const revenue = [8200, 8650, 8400, 9100, 9480, 9230, 10150, 10600, 10380, 11420, 11900, 12480];

export function RevenueNote() {
  return (
    <p className="text-sm">
      Revenue rose all quarter{" "}
      <Sparkline
        data={revenue}
        width={64}
        height={16}
        className="align-[-0.2em]"
        valueFormat="currency"
        locale="en-US"
        label="Revenue, last 12 weeks"
      />{" "}
      and closed at $12,480.
    </p>
  );
}
```

The props you will reach for first:

- `type`: `"line"`, `"area"` (a line over a soft fill), `"bar"` or `"win-loss"` (equal ticks up for positive values and down for negative ones, for streaks or daily goals).
- `width` and `height` in px. `width={0}` fills the parent, which then needs a width of its own.
- `tone` colors the line (neutral ink by default). `trendColor` colors it by direction instead: green when the last value is above the first, red when it is below.
- `invertTrend` for metrics where down is good, such as costs, churn or response time.
- `reference`: a dashed hairline at the `"first"` value, the `"average"` or `"zero"`.
- `data` can also hold rows with a `label` or an ISO `date` for the readout, and `null` leaves a gap.

Turn on `interactive` and the sparkline becomes one tab stop you can read point by point. Hover it, drag across it with a finger, or focus it and use the arrow keys.

> Live demo (Sparkline): Hover the line or drag a finger across it: the readout shows each week and the amount on the right follows. Tab to it and press Left and Right to step through the points. Try it at [Sparkline](https://wingo-ui.com/components/sparkline) and install it with `npx wingo-ui@latest add sparkline`.

## How do I put a sparkline in a table cell?

Give every row the same scale. A sparkline in a table cell scales to its own minimum and maximum by default, so every line fills its box: a client that went from $9,000 to $9,400 looks as steep as one that doubled. Pass shared `min` and `max` to every row and a taller line means more revenue. The trade-off: a small client's line sits nearly flat near the bottom. When the shape of each row matters more than its size, keep the per-row scale and say so in the column header.

`SparklineWithValue` puts the current value and a [Trend Badge](https://wingo-ui.com/components/trend-badge) next to the line, which is what most table cells need. The value defaults to the last point, the badge to the percent change from first to last.

```tsx
import { SparklineWithValue } from "@/components/ui/sparkline";

type ClientRow = { id: string; name: string; weekly: number[] };

export function ClientsTable({ rows }: { rows: ClientRow[] }) {
  // one range for every row, so the lines compare
  const max = Math.max(0, ...rows.flatMap((row) => row.weekly));

  return (
    <table className="w-full text-sm">
      <caption className="sr-only">Weekly revenue per client, last 12 weeks</caption>
      <thead>
        <tr className="text-left text-xs text-muted-foreground">
          <th className="py-2 font-medium">Client</th>
          <th className="py-2 text-right font-medium">Last 12 weeks</th>
        </tr>
      </thead>
      <tbody className="divide-y divide-border">
        {rows.map((row) => (
          <tr key={row.id}>
            <td className="py-3 font-medium">{row.name}</td>
            <td className="py-3 text-right">
              <SparklineWithValue
                data={row.weekly}
                type="area"
                width={88}
                height={32}
                min={0}
                max={max}
                layout="responsive"
                valueFormat="currency"
                locale="en-US"
                label={`${row.name}, revenue over 12 weeks`}
              />
            </td>
          </tr>
        ))}
      </tbody>
    </table>
  );
}
```

`layout="responsive"` stacks the value and badge over the line below 640px and puts them in one row from there, so the cell stays narrow on a phone. The badge uses the `ghost` variant and the `sm` size by default, colored text with an arrow and no fill, which keeps a column of them quiet. If an interactive sparkline sits in a scrolling table, the readout is portaled and pinned above the line, so the table cannot clip it.

For a table with more than a handful of columns, the [Data Table](https://wingo-ui.com/components/data-table) handles sorting and paging, and the [responsive table guide](https://wingo-ui.com/blog/responsive-table-mobile-cards) shows how its rows turn into cards on a phone.

## How do I build a KPI sparkline tile?

Use the [Stat Card](https://wingo-ui.com/components/stat-card). It takes the label, the value, the delta and the sparkline values, and lays them out as a dashboard tile: the number counts up the first time it scrolls into view, the delta pill fades in, and the KPI sparkline draws from left to right.

```bash
npx wingo-ui@latest add stat-card
```

```tsx
import { Timer, Wallet } from "lucide-react";
import { StatCard, StatGroup } from "@/components/ui/stat-card";

export function Kpis({ daily, responseHours }: { daily: number[]; responseHours: number[] }) {
  return (
    <StatGroup>
      <StatCard
        label="Revenue"
        icon={<Wallet />}
        value={128450}
        format="currency"
        delta={12.4}
        deltaLabel="vs. last month"
        sparkline={daily}
        locale="en-US"
      />
      <StatCard
        label="Median first response"
        icon={<Timer />}
        value={8100}
        format="duration"
        delta={-8.5}
        deltaLabel="vs. last month"
        invertTrend
        sparkline={responseHours}
        sparklineType="bar"
        locale="en-US"
      />
    </StatGroup>
  );
}
```

`format="duration"` takes seconds, so 8100 reads "2 hr, 15 min". With `invertTrend` the drop in response time shows green while the arrow still points down. `StatGroup` lays the tiles out two per row on phones and four from 1024px.

The card's built-in sparkline is decorative: the card already speaks one sentence with the value and the change, so the chart is hidden from screen readers and cannot be inspected. When you want the reader to scrub the line, pass the standalone Sparkline through the `chart` prop:

```tsx
import { StatCard } from "@/components/ui/stat-card";
import { Sparkline } from "@/components/ui/sparkline";

const revenue = [8200, 8650, 8400, 9100, 9480, 9230, 10150, 10600, 10380, 11420, 11900, 12480];

<StatCard
  label="Revenue"
  value={12480}
  format="currency"
  delta={52.2}
  deltaLabel="vs. 12 weeks ago"
  locale="en-US"
  chart={
    <Sparkline data={revenue} type="area" width={0} height={48} interactive valueFormat="currency" locale="en-US" label="Revenue, last 12 weeks" />
  }
/>
```

Press Simulate a new day in the demo below: a new day joins the sparkline and the value and delta count to their new numbers from where they are.

> Live demo (Stat Card): Press Simulate a new day: the sparkline gains a point and the value and the delta count to their new numbers. Try it at [Stat Card](https://wingo-ui.com/components/stat-card) and install it with `npx wingo-ui@latest add stat-card`.

## What goes in the delta chip next to a sparkline?

An arrow, a signed number and a color that reflects meaning instead of sign. The line shows the shape, and the chip gives the number people quote. The [Trend Badge](https://wingo-ui.com/components/trend-badge) is that chip on its own, for places where you print the value yourself.

```tsx
import { TrendBadge } from "@/components/ui/trend-badge";

<TrendBadge value={12.4} label="vs. last month" locale="en-US" />
<TrendBadge value={-3200} format="currency" variant="ghost" size="sm" locale="en-US" />
<TrendBadge value={-0.8} invertTrend label="churn vs. last month" locale="en-US" />
```

The first prints "+12.4%" in green, the second "−$3,200" in red, and the third a green "−0.8%" because lower churn is good. A few props worth knowing:

- `format`: `"percent"` (12.4 is +12.4%), `"number"` or `"currency"`. Values of a million and up go compact ("+$1.3M").
- `flatThreshold` (0.05 by default): smaller changes count as no change and turn gray, so +0.01% does not flash green.
- `neutralTone` paints every direction in the neutral ink, for metrics where up is neither good nor bad, like open tickets.
- `animated` is off by default, so a table of fifty badges does not all count up at once. Turn it on for a single hero number.

Screen readers hear the direction in words ("Up 12.4% vs. last month"), never the arrow glyph. Change the sentences through `labels` when you translate.

> Live demo (Trend badge): Press Change to cycle the value: the pill glides to each new number, the arrow flips with the direction, and a value under the threshold turns gray. Try it at [Trend badge](https://wingo-ui.com/components/trend-badge) and install it with `npx wingo-ui@latest add trend-badge`.

## How do I make a sparkline accessible?

Say the trend in words. A read-only Sparkline renders with `role="img"` and an accessible name made from your `label` plus a summary of the first value, the last value and the change: "Revenue, last 12 weeks: from $8,200 to $12,480, +52.2%". Pass `labels.summary` to word it in another language.

With `interactive` on, the sparkline is one tab stop. Left and Right walk the points, Home and End jump to the ends, Page Up and Page Down move by 10, and Escape clears the readout. A polite live region reads each point as you move. On the color side, `trendColor` is never the only signal: the delta chip prints the sign and an arrow, which matters for readers who cannot tell red from green. Under reduced motion the draw-in and the count-up are skipped and everything appears final.

## How does a sparkline work on a phone?

The default 28px height fits a list row, and an interactive sparkline treats the whole row height (8px above and below the drawing) as its touch target. Tap and drag sideways to scrub; a vertical drag still scrolls the page. The readout sits above the line instead of under the finger, and a second tap on the same point or a tap outside clears it. The same rules about touch targets and gestures run through the [component guides category](https://wingo-ui.com/blog/category/components) and the [React form components guide](https://wingo-ui.com/blog/react-form-components-guide).

## What are the common React sparkline pitfalls?

Six mistakes show up once real data arrives.

- **A separate scale per row.** Every line fills its box and the rows stop comparing. Share `min` and `max`.
- **Importing the wrong Sparkline.** `stat-card.tsx` exports its own small `Sparkline` (the decorative one inside the card). For tables and `interactive`, import from `@/components/ui/sparkline`.
- **A series that starts at zero.** A percent change from 0 is undefined, so the badge shows "–". Pass `delta` yourself or use `deltaFormat="number"`, which prints last minus first.
- **Too many bars.** Above 120 points, `type="bar"` and `"win-loss"` fall back to a line, with a warning in development. Aggregate daily data to weeks first.
- **`width={0}` in a parent without a width.** A fill-width sparkline measures its parent; inside an auto-width inline element it has nothing to fill. Use a grid cell or a `flex-1` box.
- **Green for a cost increase.** Set `invertTrend` on costs, churn and response time, on both the sparkline and the badge (`SparklineWithValue` passes it through).

The dashboard side of this, KPI rows over a filtered table, is built end to end in the [Next.js admin dashboard tutorial](https://wingo-ui.com/blog/nextjs-admin-dashboard-tutorial), and the [dashboard library comparison](https://wingo-ui.com/blog/best-react-component-library-for-dashboard) covers which libraries ship KPI cards at all.

## How do I add it to my project?

Run `npx wingo-ui@latest add sparkline stat-card trend-badge`. The CLI copies the source into `components/ui`, adds the helpers they import (the chart kit, number formatting, tones) and installs `motion`, `d3-shape`, `d3-scale` and `lucide-react`. All three are Pro components ([pricing](https://wingo-ui.com/pricing)). If you build with Claude Code or Cursor, the [MCP server](https://wingo-ui.com/docs/mcp) lets the agent read these props instead of guessing them.

## Components in this post

- [Sparkline](https://wingo-ui.com/components/sparkline) (Pro): A React sparkline: a word-sized chart for table cells, list rows, KPI tiles and running text, as a line, an area, bars or win / loss ticks. Install: `npx wingo-ui@latest add sparkline`
- [Stat Card](https://wingo-ui.com/components/stat-card) (Pro): A React stat card for dashboards: a label, a big number that counts up, a delta pill that knows good from bad, a sparkline and a goal bar. Install: `npx wingo-ui@latest add stat-card`
- [Trend badge](https://wingo-ui.com/components/trend-badge) (Pro): A React trend badge, the delta chip for KPIs, table cells and leaderboards: an arrow, a signed change and a color that says if it is good. Install: `npx wingo-ui@latest add trend-badge`

## FAQ

### What is a sparkline chart?

A sparkline is a tiny line or bar chart without axes, labels or a legend, sized to sit next to a word or a number. Edward Tufte named it and describes it as a word-sized graphic that can go anywhere a word or number can, such as a sentence, a table or a headline.

### How do I add a sparkline to a table cell in React?

Render a fixed-size sparkline (for example 88 by 32 px) in the cell, next to the current value and a delta chip. Pass the same min and max to every row so a taller line means a larger number; with a separate scale per row, every line fills its box and the rows stop comparing.

### Should a sparkline have a tooltip?

Only when people need exact values. A read-only sparkline with the last value printed beside it is enough for most tables; turn on inspection (interactive in the Wingo UI Sparkline) when readers want to check a week, and make it work with the keyboard and touch too.

### How do I make a sparkline accessible?

Give it role="img" and an accessible name that states the trend in words, for example "Revenue, last 12 weeks: from $8,200 to $12,480, +52.2%". Do not rely on green and red alone: print the value and a signed change as text.

### Is the Wingo UI Sparkline free?

No. The Sparkline, the Stat Card and the Trend Badge are Pro components. The Number format helper they share is free, and so is the Animated Counter that the Stat Card uses for its roll animation.

---

Source: https://wingo-ui.com/blog/react-sparkline
