Area Chart
A composable area chart with gradient fills, tooltips, and hover interactions
Preview
Installation
pnpm dlx shadcn@latest add @bklit/area-chartUsage
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.
| Prop | Type | Default | Description |
|---|---|---|---|
data | Record<string, unknown>[] | required | Array of data points |
xDataKey | string | "date" | Key in data for x-axis values |
margin | Partial<Margin> | { top: 40, right: 40, bottom: 40, left: 40 } | Chart margins |
animationDuration | number | 1100 | Clip-reveal duration in ms (cubic-bezier(0.85, 0, 0.15, 1)) |
status | "loading" | "ready" | "ready" | Loading ↔ ready choreography on one chart instance |
loadingLabel | string | — | Centered shimmer label while status="loading" ("" hides it) |
yDomainTween | boolean | true | Animate y-domain when status or target domain changes |
yDomainTweenDuration | number | 500 | Y-domain tween duration in ms |
xDomain | [Date, Date] | — | Visible x-range for brush zoom |
xDomainSlotCount | number | — | Full dataset length for x-scale padding when xDomain is set |
tweenYDomainOnXDomainChange | boolean | false | Tween y-domain when the brush changes the visible x-range |
aspectRatio | string | "2 / 1" | CSS aspect ratio |
className | string | "" | Additional CSS class |
style | CSSProperties | — | Inline container styles (e.g. fixed height for a brush strip) |
Area
Renders a filled area on the chart with a gradient fill.
| Prop | Type | Default | Description |
|---|---|---|---|
dataKey | string | required | Key in data for y values |
yAxisId | string | number | "left" | Y-scale group for biaxial charts (pair with YAxis) |
fill | string | var(--chart-line-primary) | Gradient fill color |
fillOpacity | number | 0.4 | Fill opacity at the top |
stroke | string | Same as fill | Line stroke color |
strokeWidth | number | 2 | Line stroke width |
curve | CurveFactory | curveMonotoneX | D3 curve function |
animate | boolean | true | Enable grow animation |
showLine | boolean | true | Show stroke line on top |
showHighlight | boolean | true | Show highlight on hover |
gradientToOpacity | number | 0 | Opacity at bottom of gradient |
fadeEdges | boolean | false | Fade area fill at left/right edges |
showMarkers | boolean | false | Render scatter-style ring markers at each point |
loadingStroke | string | var(--foreground) | Pulse stroke color while chart is loading |
loadingStrokeOpacity | number | 0.5 | Pulse stroke opacity while chart is loading |
markers | SeriesPointMarkerStyle | — | Marker styling (same options as Scatter) |
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.
| Prop | Type | Default | Description |
|---|---|---|---|
dataKey | string | required | Key in data for y values |
fill | string | required | Fill color or pattern URL (e.g. url(#pattern-id)) |
curve | CurveFactory | curveMonotoneX | D3 curve function |
Grid
Renders grid lines.
| Prop | Type | Default | Description |
|---|---|---|---|
horizontal | boolean | true | Show horizontal lines |
vertical | boolean | false | Show vertical lines |
numTicksRows | number | 5 | Number of horizontal lines |
numTicksColumns | number | 10 | Number of vertical lines |
stroke | string | var(--chart-grid) | Line color while ready |
loadingStroke | string | — | Grid stroke while loading chrome is active |
strokeDasharray | string | "4,4" | Dash pattern |
shimmer | boolean | false | Animate a shimmer band across horizontal grid lines |
shimmerStroke | string | color-mix(…) on --foreground at 68% | Shimmer band color and opacity |
shimmerLength | number | 140 | Shimmer band width in pixels |
shimmerSpeed | number | 1 | Shimmer speed multiplier when sync is off (higher = faster) |
shimmerSync | boolean | false | Match 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.
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.
| Prop | Type | Default | Description |
|---|---|---|---|
numTicks | number | 5 | Number of tick labels when tickMode is "domain" |
tickerHalfWidth | number | 50 | Fade radius for labels |
tickMode | "domain" | "data" | "domain" | "domain" for evenly spaced ticks; "data" for one label per row |
ChartTooltip
Renders the tooltip with crosshair, dots, and content box.
| Prop | Type | Default | Description |
|---|---|---|---|
showDatePill | boolean | true | Show animated date ticker |
showCrosshair | boolean | true | Show vertical crosshair |
showDots | boolean | true | Show dots on series |
indicatorColor | string | (point) => string | — | Crosshair and dot color |
indicatorDasharray | string | — | Dash pattern for the crosshair (e.g. "4,4") |
indicatorFadeEdges | "both" | "top" | "bottom" | "none" | "both" | Vertical crosshair fade |
indicatorFadeLength | number | 10 | Fade size (% of height) |
matchCrosshair | boolean | false | Panel uses crosshair spring when true |
damping | number | 20 | Panel follow when matchCrosshair={false}; 0 = instant |
content | (props) => ReactNode | - | Custom content renderer |
rows | (point) => TooltipRow[] | - | Custom row generator |
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.
Preview
Open in Studio<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 @ncdai/shimmering-text.
Preview
Open in Studioconst [status, setStatus] = useState<"loading" | "ready">("loading");<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} strokeWidth={2}/></AreaChart>Toggle Loading / Ready in the preview to replay the transition. 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. The components tree exposes Grid, Label, and Area:
| Layer | Controls |
|---|---|
| Grid | Grid and shimmer color pickers, shimmer toggle, band length, Animation (sync with pulse, speed when unsynced) |
| Label | Shimmer label text |
| Area | Pulse stroke color and opacity |
Data and animation panels stay collapsed in loading mode; scramble data is disabled. See the area chart gallery (Loading example).
Add @ncdai to your registry config (https://chanhdai.com/r/{name}.json) — installing @bklit/area-chart pulls in @ncdai/shimmering-text automatically.
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.
| Prop | Type | Default | Description |
|---|---|---|---|
dashFromIndex | number | — | Inclusive data index where the dashed tail begins |
dashArray | string | "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
| Prop | Type | Default | Description |
|---|---|---|---|
items | ChartMarker[] | required | Array of markers |
size | number | 28 | Marker circle size |
showLines | boolean | true | Show vertical guide lines |
animate | boolean | true | Animate 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
| Prop | Type | Default | Description |
|---|---|---|---|
fill | string | var(--chart-segment-background) | Fill color for the selected region |
SegmentLineFrom / SegmentLineTo
| Prop | Type | Default | Description |
|---|---|---|---|
stroke | string | var(--chart-segment-line) | Line color |
strokeWidth | number | 1 | Line width |
variant | "dashed" | "solid" | "gradient" | "dashed" | Line style |
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-foreground: oklch(0.985 0 0);
--chart-tooltip-muted: oklch(0.65 0.01 260);
--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