Area Chart

A composable area chart with gradient fills, tooltips, and hover interactions

Installation

pnpm dlx shadcn@latest add @bklit/area-chart

Usage

The Area Chart uses the same composable API as the Line Chart. See the charts gallery for interactive examples.

import { AreaChart, Area, Grid, XAxis, ChartTooltip } from "@bklitui/ui/charts";

const data = [
  { date: new Date("2025-01-01"), revenue: 12000, costs: 8500 },
  { date: new Date("2025-01-02"), revenue: 13500, costs: 9200 },
  // ... more data
];

export default function RevenueChart() {
  return (
    <AreaChart data={data}>
      <Grid horizontal />
      <Area dataKey="revenue" fill="var(--chart-line-primary)" />
      <Area dataKey="costs" fill="var(--chart-line-secondary)" />
      <XAxis />
      <ChartTooltip />
    </AreaChart>
  );
}

Components

AreaChart

The root component that provides context to all children. It shares the same props as LineChart.

PropTypeDefaultDescription
dataRecord<string, unknown>[]requiredArray of data points
xDataKeystring"date"Key in data for x-axis values
marginPartial<Margin>{ top: 40, right: 40, bottom: 40, left: 40 }Chart margins
animationDurationnumber1100Clip-reveal duration in ms (cubic-bezier(0.85, 0, 0.15, 1))
status"loading" | "ready""ready"Loading ↔ ready choreography on one chart instance
loadingLabelstring—Centered shimmer label while status="loading" ("" hides it)
yDomainTweenbooleantrueAnimate y-domain when status or target domain changes
yDomainTweenDurationnumber500Y-domain tween duration in ms
xDomain[Date, Date]—Visible x-range for brush zoom
xDomainSlotCountnumber—Full dataset length for x-scale padding when xDomain is set
tweenYDomainOnXDomainChangebooleanfalseTween y-domain when the brush changes the visible x-range
aspectRatiostring"2 / 1"CSS aspect ratio
classNamestring""Additional CSS class
styleCSSProperties—Inline container styles (e.g. fixed height for a brush strip)

Area

Renders a filled area on the chart with a gradient fill.

PropTypeDefaultDescription
dataKeystringrequiredKey in data for y values
yAxisIdstring | number"left"Y-scale group for biaxial charts (pair with YAxis)
fillstringvar(--chart-line-primary)Gradient fill color
fillOpacitynumber0.4Fill opacity at the top
strokestringSame as fillLine stroke color
strokeWidthnumber2Line stroke width
curveCurveFactorycurveMonotoneXD3 curve function
animatebooleantrueEnable grow animation
showLinebooleantrueShow stroke line on top
showHighlightbooleantrueShow highlight on hover
gradientToOpacitynumber0Opacity at bottom of gradient
fadeEdgesbooleanfalseFade area fill at left/right edges
showMarkersbooleanfalseRender scatter-style ring markers at each point
loadingStrokestringvar(--foreground)Pulse stroke color while chart is loading
loadingStrokeOpacitynumber0.5Pulse stroke opacity while chart is loading
markersSeriesPointMarkerStyle—Marker styling (same options as Scatter)
showValuebooleanfalseShow value labels above sampled points (see Value labels)
valueLabelMinCountnumber2Preferred minimum labels per series
valueLabelMaxCountnumberautomaticMaximum labels per series
valueLabelFormatter(value: number) => stringEnglish integersLabel text
valueLabelColorstringseries strokeLabel color
fadeValueLabelsbooleanfalseApply this series' fadeEdges to its labels

PatternArea

Renders a filled area using an SVG pattern (url(#id)). Define the pattern (e.g. PatternLines) as a child of AreaChart, then pair PatternArea with an Area that has fillOpacity={0} for the stroke line.

PropTypeDefaultDescription
dataKeystringrequiredKey in data for y values
fillstringrequiredFill color or pattern URL (e.g. url(#pattern-id))
curveCurveFactorycurveMonotoneXD3 curve function

Grid

Renders grid lines.

PropTypeDefaultDescription
horizontalbooleantrueShow horizontal lines
verticalbooleanfalseShow vertical lines
numTicksRowsnumber5Number of horizontal lines
numTicksColumnsnumber10Number of vertical lines
strokestringvar(--chart-grid)Line color while ready
loadingStrokestring—Grid stroke while loading chrome is active
strokeDasharraystring"4,4"Dash pattern
shimmerbooleanfalseAnimate a shimmer band across horizontal grid lines
shimmerStrokestringcolor-mix(…) on --foreground at 68%Shimmer band color and opacity
shimmerLengthnumber140Shimmer band width in pixels
shimmerSpeednumber1Shimmer speed multiplier when sync is off (higher = faster)
shimmerSyncbooleanfalseMatch shimmer timing to the line pulse (2.2s cycle + 280ms pause)

Background

Pattern fill for the plot area when you omit Grid. See the Background utility and Pattern Background examples on the area chart gallery.

TrendLine

Least-squares regression line over one series. Place it after the Area so it draws on top. See the Trend Line utility for stroke styles and edge fade, and the Trend Line examples on the area chart gallery.

<Area dataKey="desktop" />
<TrendLine dataKey="desktop" fadeEdges />

YAxis

Value labels on the left or right. See Y Axis for yAxisId, orientation, and biaxial usage.

XAxis

Renders x-axis labels that fade when the crosshair passes.

PropTypeDefaultDescription
numTicksnumber | ResponsiveChartLabelCount5Number of tick labels to show. Pass { base, sm, md, lg, xl } to vary it by chart width (640 / 768 / 1024 / 1280 px)
tickerHalfWidthnumber50Fade radius for labels
tickMode"data" | "domain""data""data" snaps labels to data rows (crosshair-aligned); "domain" for calendar-even spacing
labelsstring[]—Tick text per data row (overrides the default date formatting)

ChartTooltip

Renders the tooltip with crosshair, dots, and content box.

PropTypeDefaultDescription
showDatePillbooleantrueShow animated date ticker
showCrosshairbooleantrueShow vertical crosshair
showDotsbooleantrueShow dots on series
indicatorColorstring | (point) => string—Crosshair and dot color
indicatorDasharraystring—Dash pattern for the crosshair (e.g. "4,4")
indicatorFadeEdges"both" | "top" | "bottom" | "none""both"Vertical crosshair fade
indicatorFadeLengthnumber10Fade size (% of height)
matchCrosshairbooleanfalsePanel uses crosshair spring when true
dampingnumber20Panel follow when matchCrosshair={false}; 0 = instant
content(props) => ReactNode-Custom content renderer
rows(point) => TooltipRow[]-Custom row generator
dateLabelsstring[]—Date-pill text per data row (pair with XAxis labels)

Weekends and deadlines

Add Weekends to shade Saturday and Sunday, and Deadline to mark a due date with a fading region. Both sit behind the areas and follow brush zoom.

import { Deadline, Weekends } from "@bklitui/ui/charts";<AreaChart data={data}>  <Grid horizontal />  <Weekends />  <Deadline date={new Date(2026, 9, 15)} fillType="gradient" />  <Area dataKey="completed" fill="var(--chart-1)" />  <XAxis />  <ChartTooltip /></AreaChart>

Brush zoom

See the Brush utility docs for ChartBrushLayout and ChartBrush props.

Wrap the main chart in ChartBrushLayout, render a simplified mini chart in brushStrip, and add ChartBrush as a child of that strip. Pass xDomain, xDomainSlotCount, and tweenYDomainOnXDomainChange to the main AreaChart so the y-scale adapts as users pan and resize the brush.

<ChartBrushLayoutdata={data}enabledheight={72}brushStrip={(layout) => (  <AreaChart data={data} animationDuration={0} status="ready">    <Area dataKey="value" fillOpacity={0.15} animate={false} />    <ChartBrush      initialSelection={layout.brushSelection ?? undefined}      onSelectionChange={layout.onBrushSelectionChange}    />  </AreaChart>)}>{(layout) => (  <AreaChart    data={data}    xDomain={layout.xDomain}    xDomainSlotCount={layout.xDomainSlotCount}    tweenYDomainOnXDomainChange    yDomainTween  >    <Grid horizontal />    <Area dataKey="value" fillOpacity={0.35} />    <XAxis />    <ChartTooltip />  </AreaChart>)}</ChartBrushLayout>

Open Studio with brush enabled to tune strip height, blur, and selection pattern.

Loading state

Drive loading and ready from your data layer with a single AreaChart — one Grid, one Area, no component swap. Set status="loading" while fetching; switch to "ready" when data resolves.

Loading → ready: pulse loop on skeleton data → pulse finishes its grow, then flows out right → loading label drifts down 30px, blurs, and fades → grid y-domain tween (500ms) → clip-path reveal (cubic-bezier(0.85, 0, 0.15, 1)) → interaction enabled.

Ready → loading: ready area conceals to the right → grid y-domain tween → pulse loop and shimmer resume.

Pair Grid stroke / loadingStroke with shimmer props. Pair Area loadingStroke props. Use loadingLabel on AreaChart for centered shimmer text via @bklit/shimmering-text.

Loading revenue…
const [status, setStatus] = useState<"loading" | "ready">("loading");const [loadingStyle, setLoadingStyle] = useState<"pulse" | "sweep">("pulse");<AreaChartdata={data}status={status}loadingLabel="Loading revenue…"yDomainTween><Grid  horizontal  loadingStroke="color-mix(in oklch, var(--chart-grid) 50%, transparent)"  shimmer  shimmerSync  stroke="var(--chart-grid)"/><Area  dataKey="revenue"  fadeEdges  fill="var(--chart-line-primary)"  fillOpacity={0.35}  loadingStroke="var(--foreground)"  loadingStrokeOpacity={0.5}  loadingStyle={loadingStyle}  strokeWidth={2}/></AreaChart>

Toggle Loading / Ready in the preview to replay the transition, and Pulse / Sweep to switch the loading animation style. When target data spans a different y-range than the skeleton, yDomainTween morphs the scale before the area reveals.

Studio

Open Studio in loading mode and set State to Loading. In Settings, choose Loading style — Pulse (traveling segment) or Sweep (diagonal shimmer). The components tree exposes Grid, Label, and Area:

LayerControls
SettingsLoading style — Pulse or Sweep (Sweep turns off grid shimmer)
GridGrid and shimmer color pickers, shimmer toggle, band length, Animation (sync with pulse, speed when unsynced)
LabelShimmer label text
AreaPulse stroke color and opacity

Data and animation panels stay collapsed in loading mode; scramble data is disabled. See the area chart gallery (Loading example).

Installing @bklit/area-chart pulls in @bklit/shimmering-text automatically.

Loading style: pulse or sweep

The loading state has two animation styles, set with loadingStyle on the Area: the default "pulse" (a segment travels along the skeleton stroke) or "sweep" (a soft diagonal shimmer sweeps across the whole area). Set it on the Area inside a status="loading" chart, or on the AreaChartLoading wrapper:

<AreaChart data={data} status="loading">
  <Grid horizontal shimmer />
  <Area dataKey="revenue" loadingStyle="sweep" />
</AreaChart>

// or, with the turnkey wrapper:
<AreaChartLoading loadingStyle="sweep" />;

The sweep masks over the real skeleton stroke, so it follows whatever curve the Area uses and respects prefers-reduced-motion. The sweep is used only during steady loading; the pulse still drives the exit transition. See the Loading (Sweep) example on the area chart gallery.

Dashed tail

Set dashFromIndex on Area to draw a solid stroke through one data point, then a dashed segment through the end of the series. Useful when the final period is still in progress (e.g. yesterday → today).

dashFromIndex is inclusive — dashing starts at that row and continues through the last point. The dashed segment follows the same curved path as the solid stroke and respects the stroke gradient fade from fadeEdges.

PropTypeDefaultDescription
dashFromIndexnumber—Inclusive data index where the dashed tail begins
dashArraystring"6,4"SVG stroke-dasharray pattern for the tail segment
<Area
  dataKey="visitors"
  dashFromIndex={5}
  dashArray="6,4"
  fill="var(--chart-line-primary)"
  fillOpacity={0.35}
/>

Markers

Add markers to annotate specific dates on the chart:

import {
  AreaChart,
  Area,
  ChartTooltip,
  ChartMarkers,
  MarkerTooltipContent,
  useActiveMarkers,
  type ChartMarker,
} from "@bklitui/ui/charts";

const markers: ChartMarker[] = [
  {
    date: new Date("2025-01-05"),
    icon: "🚀",
    title: "v1.2.0 Released",
    description: "New chart animations",
  },
];

function MyChart({ data }) {
  return (
    <AreaChart data={data}>
      <Area dataKey="revenue" fill="var(--chart-line-primary)" />
      <ChartMarkers items={markers} />
      <ChartTooltip>
        <MarkerContent markers={markers} />
      </ChartTooltip>
    </AreaChart>
  );
}

function MarkerContent({ markers }) {
  const activeMarkers = useActiveMarkers(markers);
  if (activeMarkers.length === 0) return null;
  return <MarkerTooltipContent markers={activeMarkers} />;
}

ChartMarker Interface

interface ChartMarker {
  date: Date;
  icon: React.ReactNode;
  title: string;
  description?: string;
  content?: React.ReactNode;
  color?: string;
  onClick?: () => void;
  href?: string;
  target?: "_blank" | "_self";
}

ChartMarkers Props

PropTypeDefaultDescription
itemsChartMarker[]requiredArray of markers
sizenumber28Marker circle size
showLinesbooleantrueShow vertical guide lines
animatebooleantrueAnimate markers on entrance

Segment Selection

Add click-drag and touch segment selection with composable components. The area highlight automatically shows the selected path segment.

Basic Usage

import {
  AreaChart,
  Area,
  Grid,
  XAxis,
  ChartTooltip,
  SegmentBackground,
  SegmentLineFrom,
  SegmentLineTo,
} from "@bklitui/ui/charts";

<AreaChart data={data}>
  <Grid horizontal />
  <Area dataKey="revenue" fill="var(--chart-line-primary)" />
  <SegmentBackground />
  <SegmentLineFrom />
  <SegmentLineTo />
  <XAxis />
  <ChartTooltip />
</AreaChart>

Use SegmentBackground, SegmentLineFrom, and SegmentLineTo independently — you do not need all three. Boundary lines support variant="dashed" | "solid" | "gradient".

Reading Selection Data

Use the useChart hook inside a child component to read the active selection:

import { useChart } from "@bklitui/ui/charts";

function SelectionStats({ onSelectionChange }) {
  const { selection, data, xAccessor } = useChart();

  useEffect(() => {
    if (!selection?.active) {
      onSelectionChange(null);
      return;
    }

    const startPoint = data[selection.startIndex];
    const endPoint = data[selection.endIndex];
    onSelectionChange({ startPoint, endPoint });
  }, [selection, data, xAccessor, onSelectionChange]);

  return null;
}

SegmentBackground

PropTypeDefaultDescription
fillstringvar(--chart-segment-background)Fill color for the selected region

SegmentLineFrom / SegmentLineTo

PropTypeDefaultDescription
strokestringvar(--chart-segment-line)Line color
strokeWidthnumber1Line width
variant"dashed" | "solid" | "gradient""dashed"Line style

Value labels

Enable labels on each Area independently. Labels use measured text bounds, stay inside the plot, and share collision detection across series. Crowded labels are hidden; full values remain available in the tooltip. Labels render above all area fills and do not inherit fadeEdges unless fadeValueLabels is enabled. The renderer samples labels from the visible data and adjusts the count to the plot width. valueLabelMinCount is a preference; available space and collisions can reduce it. valueLabelMaxCount is a strict per-series ceiling. All enabled series use the same final label count so comparisons stay aligned.

<AreaChart data={data}>
  <Area dataKey="net" showValue valueLabelMinCount={2} valueLabelMaxCount={5} />
  <Area dataKey="gross" showValue valueLabelMinCount={2} valueLabelMaxCount={5} />
  <ChartTooltip />
</AreaChart>
Prop on AreaTypeDefaultDescription
showValuebooleanfalseEnable labels; set false to disable them
valueLabelMinCountnumber2Preferred minimum per series; space and collisions take priority
valueLabelMaxCountnumberautomaticStrict maximum per series; 0 hides all labels and takes precedence over minimum
valueLabelFormatter(value: number) => stringEnglish integer formattingCustom complete label text
valueLabelColorstringseries strokeLabel color
fadeValueLabelsbooleanfalseExplicitly apply the series edge fade to labels

Labels are opt-in, so charts without showValue keep their existing rendering. Value labels are drawn by AreaChart; showValue on an Area inside ComposedChart is currently ignored. Use valueLabelFormatter for currency, units, or localized digits. The SVG labels are visual annotations; provide a chart summary or accessible data table when people need the underlying values without relying on the visual plot.

Open Studio with value labels to adjust the label count and preview multiple series at different widths.

Theming

The Area Chart uses the same CSS variables as the Line Chart:

:root {
  --chart-background: oklch(1 0 0);
  --chart-foreground: oklch(0.145 0.004 285);
  --chart-foreground-muted: oklch(0.55 0.014 260);
  --chart-line-primary: oklch(0.623 0.214 255);
  --chart-line-secondary: oklch(0.705 0.015 265);
  --chart-crosshair: oklch(0.4 0.1828 274.34);
  --chart-grid: oklch(0.9 0 0);
  --chart-tooltip-background: var(--popover);
  --chart-tooltip-foreground: var(--popover-foreground);
  --chart-tooltip-muted: var(--muted-foreground);
  --chart-marker-background: oklch(0.97 0.005 260);
  --chart-marker-border: oklch(0.85 0.01 260);
  --chart-marker-foreground: oklch(0.3 0.01 260);
  --chart-marker-badge-background: oklch(0 0 0);
  --chart-marker-badge-foreground: oklch(1 0 0);
  --chart-segment-background: oklch(0.5 0 0 / 0.06);
  --chart-segment-line: oklch(0.5 0 0 / 0.25);
}

.dark {
  --chart-background: oklch(0.145 0 0);
  --chart-foreground: oklch(0.45 0 0);
  --chart-crosshair: oklch(0.45 0 0);
  --chart-grid: oklch(0.25 0 0);
  --chart-marker-background: oklch(0.25 0.01 260);
  --chart-marker-border: oklch(0.4 0.01 260);
  --chart-marker-foreground: oklch(0.9 0 0);
  --chart-marker-badge-background: oklch(1 0 0);
  --chart-marker-badge-foreground: oklch(0.15 0 0);
  --chart-segment-background: oklch(1 0 0 / 0.06);
  --chart-segment-line: oklch(1 0 0 / 0.25);
}

Dependencies

This component requires the same packages as the Line Chart:

pnpm add @visx/shape @visx/curve @visx/scale @visx/gradient @visx/responsive @visx/event @visx/grid d3-array motion react-use-measure