Sparkline

Tiny line, area and bar charts built for density — one path per chart, a shared resize observer, lightweight enter animations and a minimal hover crosshair

Preview
RevenueDay 24
4.6K↑ 1.5%
VisitorsDay 24
1.3K↓ 33.2%
OrdersDay 18
170↑ 31.8%

Installation

pnpm dlx shadcn@latest add @bklit/sparkline

Usage

Sparklines are single-element charts. Pass data and a dataKey — no axes, grid or tooltip to configure.

import { AreaSparkline, BarSparkline, LineSparkline } from "@bklitui/ui/charts";

<LineSparkline data={rows} dataKey="value" />
<AreaSparkline data={rows} dataKey="value" fill="gradient" />
<BarSparkline data={rows} dataKey="value" barRadius={2} />
  • Sizing: omit width to fill the parent; height defaults to 32. Every responsive sparkline on the page shares one ResizeObserver. Pass a fixed width to skip measuring entirely.
  • Animation: the enter animation is on by default (animate). Lines draw in along their stroke, areas wipe in, bars grow from the baseline. When data changes, the path morphs to the new values over 500ms (dataTransitionDuration), like the main charts — written straight to the DOM, so no React re-render per frame. With prefers-reduced-motion: reduce the enter becomes a short fade and data changes snap.
  • Hover highlight: like the main line and area charts, hovering fades the series to inactiveOpacity (0.5) and re-strokes the segment around the hovered point at full strength; the bright segment springs as you move. Bars fade every bar except the focused one (inactiveOpacity, 0.4). Turn the line/area highlight off with showHighlight={false}.
  • Hover: a crosshair ("dashed" by default) and a dot spring to the nearest point, with the same spring as the main charts' tooltip (hoverTransition={false} to snap; reduced motion always snaps). Springs only exist while a sparkline is hovered. There is deliberately no tooltip — read the hovered value with useSparklineHover and render it wherever you like.

Hovered values

useSparklineHover lifts hover state out of the chart. Spread sparklineProps onto a sparkline and read datum or index:

const hover = useSparklineHover<Row>();
const shown = hover.datum ?? rows.at(-1);

<span>{shown.value}</span>
<AreaSparkline data={rows} dataKey="value" {...hover.sparklineProps} />

Spread the same props onto several sparklines to sync their crosshairs:

Revenue
4.6K
Visitors
1.3K
Orders
176

Crosshair styles

dashed
solid
gradient

Hover feel

The crosshair and dot spring between points with the same spring as the main charts' tooltip. Pass only the values you want to change to springConfig — higher stiffness feels snappier, lower damping bouncier — or set hoverTransition={false} to snap straight to each point. The series fade and highlight still animate either way. Reduced motion always snaps.

<LineSparkline data={rows} dataKey="value" />
<LineSparkline data={rows} dataKey="value" springConfig={{ stiffness: 600, damping: 45 }} />
<LineSparkline data={rows} dataKey="value" hoverTransition={false} />
<BarSparkline data={rows} dataKey="value" hoverTransition={false} />
Spring (default)
Snappy
No spring

Many on one page

Each sparkline renders a single <path> (bars included), animates with the Web Animations API, and only the hovered instance re-renders. 200 instances:

Migrating

RechartsTanStack Chartsbklit
<LineChart data><Line dataKey="v" dot={false} /></LineChart>lineY(rows, { y: "v" })<LineSparkline data dataKey="v" />
<AreaChart data><Area dataKey="v" /></AreaChart>areaY(rows, …) + lineY(rows, …)<AreaSparkline data dataKey="v" />
<BarChart data><Bar dataKey="v" /></BarChart>barY(rows, { y: "v" })<BarSparkline data dataKey="v" />
isAnimationActivesvgAnimation / motion()animate
type="monotone"curvecurve="monotone"
ResponsiveContainer / responsivewidth / heightomit width

Components

LineSparkline

PropTypeDefaultDescription
dataT[]—Rows to plot
dataKeykeyof T | (d, i) => number—Numeric field or accessor
widthnumberparent widthFixed width in px
heightnumber32Height in px
colorstringvar(--chart-line-primary)Series colour
strokeWidthnumber1.5Line width
curve"linear" | "monotone" | "natural" | "step""monotone"Interpolation
endDotbooleanfalseDot on the last point when idle
showHighlightbooleantrueFade the series and highlight the hovered segment
inactiveOpacitynumber0.5Series opacity while hovering
paddingnumber | { top, right, bottom, left }4Inner padding
yDomain[min?, max?]data extentOverride the value domain
animatebooleantruePlay the enter animation
animationDurationnumber900Enter duration in ms
animationEasingstringcubic-bezier(0.85, 0, 0.15, 1)CSS easing
dataTransitionDurationnumber500Morph to new data in ms; 0 snaps (requires animate)
crosshair"dashed" | "solid" | "gradient" | false"dashed"Hover guide line
dotbooleantrueDot on the hovered point
hoverTransitionbooleantrueSpring the crosshair and dot between points
springConfig{ stiffness?, damping? }{ stiffness: 300, damping: 30 }Crosshair/dot spring — pass only what you change, e.g. { damping: 40 }
interactivebooleantrueAttach pointer listeners
hoverIndexnumber | null—Controlled hovered index
onHoverChange(index, datum) => void—Hover callback
aria-labelstringgenerated summaryAccessible label

AreaSparkline

Everything on LineSparkline, plus:

PropTypeDefaultDescription
fill"gradient" | "solid""gradient"Fill style
fillOpacitynumber0.3Peak fill opacity

BarSparkline

Shares the base props (no curve, strokeWidth or endDot; padding defaults to 2). Bars always include zero and support negative values.

PropTypeDefaultDescription
barGapnumber2Gap between bars in px
barRadiusnumber1Corner radius away from the baseline
inactiveOpacitynumber0.4Opacity of other bars while hovering

useSparklineHover

Returns { index, datum, isHovering, sparklineProps }.