# Sparkline (/ui/components/react/sparkline)



<Playground component="sparkline" />

## Usage [#usage]

```tsx
import { Sparkline, SparklineChart, SparklineValue, SparklineLabel } from '@appica/ui-react/sparkline'
```

```tsx
<Sparkline data={[8, 14, 10, 18, 15, 22, 19, 27]}>
  <SparklineChart variant="area" />
</Sparkline>
```

A `Sparkline` distills a series into a small, word-sized chart — the trend behind a KPI, a row in a table, a card header. Pass the numbers to the root's `data`; `SparklineChart` draws them. It comes in three layouts via `variant`: `line`, `area` (solid-filled to a baseline), and `column` (bars). A plain `line` can also take a decorative gradient `fill` down to the chart's edge.

The root holds the data and the hover state and hands them to the parts, so the optional `SparklineValue` and `SparklineLabel` can sit anywhere inside — a headline above the chart, say — and update as the pointer moves. Recolor everything through a single `color` prop, and read the hovered point from `onActiveChange` to render it wherever you like.

## Examples [#examples]

### Line [#line]

The default. A crisp stroke with a hover indicator that snaps to the nearest point. The stroke stays sharp at any width because it doesn't scale with the chart.

```tsx
import { Sparkline, SparklineChart } from '@appica/ui-react/sparkline'

export default function SparklineDefault() {
  return (
    <Sparkline data={[8, 14, 10, 18, 15, 22, 19, 27, 24, 31]} className="w-64 max-w-full">
      <SparklineChart aria-label="Trend over the last 10 periods" />
    </Sparkline>
  )
}
```

### Area [#area]

`variant="area"` is a conventional area chart: it's always filled with a solid tint between the curve and a **baseline** (`0` by default). Values below the baseline fill *below* it — as in the second chart — so it reads as a signed magnitude, not just a trend. Move the reference line with `baseline`.

```tsx
import { Sparkline, SparklineChart } from '@appica/ui-react/sparkline'

export default function SparklineArea() {
  return (
    <div className="flex w-64 max-w-full flex-col gap-6">
      <Sparkline data={[6, 11, 8, 16, 13, 22, 18, 27]}>
        <SparklineChart variant="area" height={56} aria-label="Signups over 8 weeks" />
      </Sparkline>
      <Sparkline data={[3, -4, 2, 6, -2, -6, 4, 1]}>
        <SparklineChart variant="area" height={56} aria-label="Net change over 8 weeks" />
      </Sparkline>
    </div>
  )
}
```

### Filled line [#filled-line]

A plain `line` is stroke-only. Add `fill` for a gradient that fades from the line down to the chart's bottom edge — the familiar "filled trend line" without a semantic baseline. This is the right choice for values far from zero, like a revenue KPI, where a true area chart would collapse into a solid block. (For a solid, baseline-anchored fill, use `variant="area"` instead.)

```tsx
import { Sparkline, SparklineChart } from '@appica/ui-react/sparkline'

const DATA = [12, 19, 15, 24, 20, 28, 25, 33]

export default function SparklineFill() {
  return (
    <div className="flex w-64 max-w-full flex-col gap-6">
      <Sparkline data={DATA}>
        <SparklineChart height={56} aria-label="Line" />
      </Sparkline>
      <Sparkline data={DATA}>
        <SparklineChart fill height={56} aria-label="Gradient-filled line" />
      </Sparkline>
    </div>
  )
}
```

### Columns [#columns]

`variant="column"` renders bars, one per value, that pivot on a **baseline** (`0` by default): values above it grow up, values below grow *down* — the standard signed bar sparkline, as in the second chart. Hovering highlights the active column. Best for discrete buckets — visits per day, net change per period. Move the pivot with `baseline`.

```tsx
import { Sparkline, SparklineChart } from '@appica/ui-react/sparkline'

const DAYS = ['Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat', 'Sun', 'Mon']

export default function SparklineColumn() {
  return (
    <div className="flex w-64 max-w-full flex-col gap-6">
      <Sparkline data={[4, 9, 6, 12, 8, 15, 11, 18]} labels={DAYS}>
        <SparklineChart variant="column" height={56} tooltip aria-label="Daily visits" />
      </Sparkline>
      <Sparkline data={[6, -7, 9, -4, 8, -9, 5, -6]} labels={DAYS}>
        <SparklineChart variant="column" height={56} tooltip aria-label="Net change per day" />
      </Sparkline>
    </div>
  )
}
```

### Line smoothing [#line-smoothing]

`curve` controls how round the line is, from `0` (straight segments between points) to `1` (fully smoothed peaks). It applies to `line` and `area`.

```tsx
import { Sparkline, SparklineChart } from '@appica/ui-react/sparkline'

const DATA = [10, 24, 12, 28, 16, 30, 14, 26]

export default function SparklineCurve() {
  return (
    <div className="flex w-64 max-w-full flex-col gap-6">
      <Sparkline data={DATA}>
        <SparklineChart curve={0} aria-label="Straight segments" />
      </Sparkline>
      <Sparkline data={DATA}>
        <SparklineChart curve={1} aria-label="Fully smoothed" />
      </Sparkline>
    </div>
  )
}
```

### Line weight [#line-weight]

`strokeWidth` sets the line thickness in pixels (default `2`). The stroke never scales with the chart, so it stays exactly this weight at any width.

```tsx
import { Sparkline, SparklineChart } from '@appica/ui-react/sparkline'

const DATA = [10, 24, 12, 28, 16, 30, 14, 26]

export default function SparklineStroke() {
  return (
    <div className="flex w-64 max-w-full flex-col gap-6">
      <Sparkline data={DATA}>
        <SparklineChart strokeWidth={1} aria-label="Thin line" />
      </Sparkline>
      <Sparkline data={DATA}>
        <SparklineChart strokeWidth={3} aria-label="Thick line" />
      </Sparkline>
    </div>
  )
}
```

### Tooltip [#tooltip]

Set `tooltip` to float the value at the hovered point. When the root has `labels`, the tooltip shows the matching label alongside the value; `format` controls the number's presentation.

```tsx
import { Sparkline, SparklineChart } from '@appica/ui-react/sparkline'

export default function SparklineTooltip() {
  return (
    <Sparkline
      data={[8, 15, 11, 20, 16, 26, 22]}
      labels={['Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat', 'Sun']}
      className="w-72 max-w-full"
    >
      <SparklineChart variant="area" height={56} tooltip aria-label="Weekly requests" />
    </Sparkline>
  )
}
```

### Value headline [#value-headline]

Drop `SparklineValue` and `SparklineLabel` inside the root to print the active point — or, when nothing is hovered, the last one. Combined with `format`, this is the classic "big number over a trend" stat. Since the values sit far from zero, this uses a gradient-**filled line** rather than an area chart. Hover the chart and the headline follows.

```tsx
import { Sparkline, SparklineChart, SparklineValue, SparklineLabel } from '@appica/ui-react/sparkline'

export default function SparklineStat() {
  return (
    <Sparkline
      data={[198_120, 201_540, 199_880, 205_300, 208_770, 214_260, 222_240]}
      labels={['Nov 11', 'Nov 12', 'Nov 13', 'Nov 14', 'Nov 15', 'Nov 16', 'Nov 17']}
      className="w-72 max-w-full"
    >
      <SparklineLabel />
      <SparklineValue className="text-2xl" />
      <SparklineChart fill height={56} tooltip aria-label="Active users this week" />
    </Sparkline>
  )
}
```

### Reacting to the active point [#reacting-to-the-active-point]

For full control over where the reading appears, pass `onActiveChange`. It fires with the hovered `{ index, value, label }` (or `null` on leave), so you can render the value anywhere — outside the chart, in a sibling panel, wherever.

```tsx
'use client'

import { useState } from 'react'
import { Sparkline, SparklineChart } from '@appica/ui-react/sparkline'
import type { SparklinePoint } from '@appica/ui-react/sparkline'

const DATA = [820, 932, 901, 934, 1290, 1330, 1320]
const LABELS = ['Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat', 'Sun']

export default function SparklineControlled() {
  const [active, setActive] = useState<SparklinePoint | null>(null)
  const point = active ?? { index: DATA.length - 1, value: DATA[DATA.length - 1]!, label: LABELS[DATA.length - 1] }

  return (
    <div className="w-72 max-w-full">
      <div className="mb-1 flex items-baseline justify-between">
        <span className="text-foreground-muted text-sm">{point.label}</span>
        <span className="text-foreground-intense text-lg font-semibold tabular-nums">
          {point.value.toLocaleString()}
        </span>
      </div>
      <Sparkline data={DATA} labels={LABELS} onActiveChange={setActive}>
        <SparklineChart height={48} aria-label="Weekly requests" />
      </Sparkline>
    </div>
  )
}
```

### Custom colors [#custom-colors]

`color` accepts any CSS color and drives the line, fill, indicator, and tooltip swatch. Reach for a theme token — `var(--success-emphasis)`, `var(--error-emphasis)` — so it tracks light and dark mode, or pass a raw value like `#a855f7` for a one-off.

```tsx
import { Sparkline, SparklineChart } from '@appica/ui-react/sparkline'

export default function SparklineColors() {
  return (
    <div className="flex w-64 max-w-full flex-col gap-5">
      <Sparkline data={[6, 12, 9, 16, 14, 22, 20, 28]} color="var(--success-emphasis)">
        <SparklineChart variant="area" aria-label="Revenue" />
      </Sparkline>
      <Sparkline data={[28, 22, 25, 16, 19, 12, 14, 8]} color="var(--error-emphasis)">
        <SparklineChart variant="area" aria-label="Churn" />
      </Sparkline>
      <Sparkline data={[10, 14, 11, 15, 13, 18, 15, 19]} color="var(--warning-emphasis)">
        <SparklineChart aria-label="Latency" />
      </Sparkline>
      <Sparkline data={[8, 16, 12, 22, 18, 26, 24, 32]} color="#a855f7">
        <SparklineChart fill aria-label="Engagement" />
      </Sparkline>
      <Sparkline data={[5, 11, 8, 14, 10, 18, 13, 21]} color="var(--info-emphasis)">
        <SparklineChart variant="column" aria-label="Orders" />
      </Sparkline>
    </div>
  )
}
```

## RTL [#rtl]

Every Appica UI component supports right-to-left layouts out of the box. Set the `dir` attribute on a container (commonly your `<html>` element) so CSS logical properties resolve correctly, and wrap your tree in `DirectionProvider` so direction-aware behavior follows the same direction.

```tsx
import { DirectionProvider } from '@appica/ui-react/providers/direction-provider'

export default function RootLayout({ children }) {
  return (
    <html lang="ar" dir="rtl">
      <body>
        <DirectionProvider dir="rtl">{children}</DirectionProvider>
      </body>
    </html>
  )
}
```

Under RTL the plot mirrors horizontally — the series reads right-to-left, columns stack from the right, and the hover mapping flips to match. For setup details and caveats, see the [RTL guide](/ui/docs/react/rtl).

<RtlPreview component="sparkline" />

## API reference [#api-reference]

`Sparkline` is a compound component. The root owns the data, formatting, and hover state; `SparklineChart` draws the visualization; `SparklineValue` and `SparklineLabel` print the active-or-last point. Each part forwards `ref` and its remaining native attributes to the underlying element.

### Sparkline [#sparkline]

The root and provider. Renders a `<div>` that lays its children out in a column, and exposes the accent color as the `--sparkline-color` CSS variable.

| Prop             | <ColMinWidth width="220">Type</ColMinWidth>          | Default          | <ColMinWidth width="240">Description</ColMinWidth>                              |
| ---------------- | ---------------------------------------------------- | ---------------- | ------------------------------------------------------------------------------- |
| `data`           | `number[]`                                           | —                | **Required.** The series to plot.                                               |
| `labels`         | `string[]`                                           | —                | Per-point labels (e.g. dates), surfaced in the tooltip and to `SparklineLabel`. |
| `color`          | `string`                                             | `var(--primary)` | Accent for the line, fill, indicator, and tooltip swatch. Any CSS color.        |
| `format`         | `Intl.NumberFormatOptions`                           | —                | Formatting for displayed values (`SparklineValue`, tooltip).                    |
| `locale`         | `Intl.LocalesArgument`                               | —                | Locale used by `Intl.NumberFormat`.                                             |
| `onActiveChange` | <Code>(point: SparklinePoint \| null) => void</Code> | —                | Fires when the hovered point changes; `null` on pointer leave.                  |
| `className`      | `string`                                             | —                | Extra classes, merged via `tailwind-merge`.                                     |

### SparklineChart [#sparklinechart]

The visualization. Renders a `<div role="img">` wrapping an SVG (line/area) or bars (column), plus the hover indicator and tooltip overlays.

| Prop            | <ColMinWidth width="220">Type</ColMinWidth>       | Default             | <ColMinWidth width="240">Description</ColMinWidth>                                            |
| --------------- | ------------------------------------------------- | ------------------- | --------------------------------------------------------------------------------------------- |
| `variant`       | `'line' \| 'area' \| 'column'`                    | `'line'`            | The layout.                                                                                   |
| `curve`         | `number`                                          | `0.5`               | Line smoothing from `0` (straight) to `1` (fully rounded). Line/area only.                    |
| `fill`          | `boolean`                                         | `false`             | **`line` only.** Add a gradient fill from the line to the bottom edge. Area is always filled. |
| `baseline`      | `number`                                          | `0`                 | The pivot the fill/bars grow from; values below it render below. Area and column.             |
| `height`        | `number`                                          | `48`                | Chart height, in pixels.                                                                      |
| `strokeWidth`   | `number`                                          | `2`                 | Line thickness, in pixels. Line/area only.                                                    |
| `indicator`     | `boolean`                                         | `true`              | Show the hover indicator (dot + guide, or the active-column highlight).                       |
| `tooltip`       | `boolean`                                         | `false`             | Float a tooltip at the hovered point.                                                         |
| `renderTooltip` | <Code>(point: SparklinePoint) => ReactNode</Code> | —                   | Render custom tooltip content instead of the default swatch + value. Implies `tooltip`.       |
| `aria-label`    | `string`                                          | `'{variant} chart'` | Accessible name for the graphic. Describe the trend for screen-reader users.                  |
| `className`     | `string`                                          | —                   | Extra classes on the wrapper, merged via `tailwind-merge`.                                    |

### SparklineValue [#sparklinevalue]

Prints the hovered point's value — or the last point's when nothing is hovered — formatted with the root's `format`/`locale`. Renders a `<span>`.

| Prop        | <ColMinWidth width="220">Type</ColMinWidth>                  | Default | <ColMinWidth width="240">Description</ColMinWidth>                          |
| ----------- | ------------------------------------------------------------ | ------- | --------------------------------------------------------------------------- |
| `children`  | <Code>(formatted: string, value: number) => ReactNode</Code> | —       | Render function to fully customize the displayed text. Omit for the number. |
| `className` | `string`                                                     | —       | Extra classes, merged via `tailwind-merge`.                                 |

### SparklineLabel [#sparklinelabel]

Prints the hovered (or last) point's label from the root's `labels`. Renders nothing when no `labels` are provided. Renders a `<span>`.

| Prop        | <ColMinWidth width="220">Type</ColMinWidth>                      | Default | <ColMinWidth width="240">Description</ColMinWidth>                       |
| ----------- | ---------------------------------------------------------------- | ------- | ------------------------------------------------------------------------ |
| `children`  | <Code>(label: string, point: SparklinePoint) => ReactNode</Code> | —       | Render function to customize the displayed text. Omit for the raw label. |
| `className` | `string`                                                         | —       | Extra classes, merged via `tailwind-merge`.                              |

### SparklinePoint [#sparklinepoint]

The shape passed to `onActiveChange` and the render-function children.

| Field   | Type     | Description                                  |
| ------- | -------- | -------------------------------------------- |
| `index` | `number` | Index of the point in `data`.                |
| `value` | `number` | The point's numeric value.                   |
| `label` | `string` | The point's label, if `labels` was provided. |

## Accessibility [#accessibility]

* `SparklineChart` renders with `role="img"` and an `aria-label` (defaulting to `"{variant} chart"`). Because a sparkline is a graphic, &#x2A;*pass a descriptive `aria-label`** that summarizes the trend — e.g. `"Revenue, up 40% over 7 days"` — so screen-reader users get the meaning, not just the shape.
* The indicator and tooltip are pointer affordances layered with `pointer-events-none` and marked `aria-hidden`; they never trap focus or intercept clicks. When a value needs to be readable, surface it as text via `SparklineValue` or `onActiveChange`.
* The indicator, tooltip, and column heights animate only under `motion-safe`, so they stay still when the user prefers reduced motion.
