# Area Chart Examples

> Complete area chart layouts with realistic structure and sample data.

Source: https://www.honestui.com/docs/charts/area-chart/blocks

These examples combine chart parts with headings, summaries, legends, and controls. The values are sample data. Open the Code tab to replace the data keys, labels, units, and written summary together.

## Marathon finish percentiles [#marathon-finish-percentiles]

Use this structure to compare several percentile bands across an ordered sequence. Replace `time`, `p99`, `p95`, `p75`, and `p50`, then update the visible unit and series labels. Provide a table when readers need individual percentile values without pointer hover.

### Marathon finish percentiles

```tsx
"use client";

import { AreaChart, type ChartConfig } from "honestui/charts";
import { cn } from "@/lib/utils";
import { useState } from "react";

// Scenario: Marathon finish percentiles
const SERIES = [
  { key: "p99", label: "P99", color: "#D41F12", latest: 204 },
  { key: "p95", label: "P95", color: "#F37A00", latest: 98 },
  { key: "p75", label: "P75", color: "#62C9D4", latest: 46 },
  { key: "p50", label: "P50", color: "#007292", latest: 21 },
] as const;

const chartData = [
  { time: "Race 13:06", p99: 209, p95: 102, p75: 49, p50: 22 },
  { time: "Race 13:07", p99: 218, p95: 105, p75: 51, p50: 23 },
  { time: "Race 13:08", p99: 201, p95: 99, p75: 48, p50: 22 },
  { time: "Race 13:09", p99: 213, p95: 104, p75: 52, p50: 24 },
  { time: "Race 13:10", p99: 228, p95: 110, p75: 50, p50: 23 },
  { time: "Race 13:11", p99: 208, p95: 101, p75: 49, p50: 22 },
  { time: "Race 13:12", p99: 199, p95: 98, p75: 47, p50: 21 },
  { time: "Race 13:13", p99: 220, p95: 107, p75: 51, p50: 23 },
  { time: "Race 13:14", p99: 233, p95: 112, p75: 53, p50: 24 },
  { time: "Race 13:15", p99: 215, p95: 103, p75: 50, p50: 23 },
  { time: "Race 13:16", p99: 224, p95: 108, p75: 52, p50: 24 },
  { time: "Race 13:17", p99: 239, p95: 115, p75: 54, p50: 26 },
  { time: "Race 13:18", p99: 256, p95: 124, p75: 58, p50: 26 },
  { time: "Race 13:19", p99: 309, p95: 145, p75: 63, p50: 27 },
  { time: "Race 13:20", p99: 340, p95: 158, p75: 68, p50: 28 },
  { time: "Race 13:21", p99: 321, p95: 150, p75: 64, p50: 27 },
  { time: "Race 13:22", p99: 274, p95: 131, p75: 59, p50: 26 },
  { time: "Race 13:23", p99: 240, p95: 117, p75: 54, p50: 24 },
  { time: "Race 13:24", p99: 223, p95: 108, p75: 51, p50: 23 },
  { time: "Race 13:25", p99: 214, p95: 104, p75: 50, p50: 23 },
  { time: "Race 13:26", p99: 206, p95: 100, p75: 49, p50: 22 },
  { time: "Race 13:27", p99: 221, p95: 107, p75: 51, p50: 23 },
  { time: "Race 13:28", p99: 230, p95: 111, p75: 52, p50: 24 },
  { time: "Race 13:29", p99: 212, p95: 102, p75: 50, p50: 22 },
  { time: "Race 13:30", p99: 204, p95: 99, p75: 48, p50: 22 },
  { time: "Race 13:31", p99: 218, p95: 105, p75: 51, p50: 23 },
  { time: "Race 13:32", p99: 232, p95: 112, p75: 53, p50: 24 },
  { time: "Race 13:33", p99: 220, p95: 107, p75: 51, p50: 23 },
  { time: "Race 13:34", p99: 226, p95: 109, p75: 51, p50: 23 },
];

const chartConfig = {
  p99: { label: "P99", colors: { light: ["#f87171"], dark: ["#D41F12"] } },
  p95: { label: "P95", colors: { light: ["#fbbf24"], dark: ["#F37A00"] } },
  p75: { label: "P75", colors: { light: ["#60a5fa"], dark: ["#62C9D4"] } },
  p50: { label: "P50", colors: { light: ["#93c5fd"], dark: ["#007292"] } },
} satisfies ChartConfig;

export function LatencyAreaChart() {
  const [selected, setSelected] = useState<string | null>(null);

  return (
    <div className="flex h-full w-full flex-col p-4">
      <div className="grid grid-cols-2 gap-y-2 sm:grid-cols-4 sm:gap-y-4">
        {SERIES.map(({ key, label, color, latest }) => (
          <button
            key={key}
            type="button"
            onClick={() => setSelected((prev) => (prev === key ? null : key))}
            className={cn(
              "border-border flex cursor-pointer flex-row items-center gap-1.5 px-3 text-left transition-opacity sm:flex-col sm:items-start sm:gap-1.5 sm:px-4 sm:first:pl-1 sm:[&:not(:first-child)]:border-l [&:nth-child(even)]:border-l",
              selected !== null && selected !== key && "opacity-40",
            )}
          >
            <div className="text-primary flex items-center gap-1.5 text-xs font-medium sm:gap-2">
              <span className="size-2 shrink-0 rounded-[2px]" style={{ backgroundColor: color }} />
              {label}
            </div>
            <span className="text-muted-foreground text-xs sm:hidden">–</span>
            <div className="leading-none">
              <span className="text-primary text-sm font-medium tracking-tight sm:text-xl">
                {latest}
              </span>
              <span className="text-muted-foreground ml-0.5 text-xs font-light sm:ml-1 sm:text-sm">
                ms
              </span>
            </div>
          </button>
        ))}
      </div>
      <AreaChart
        data={chartData}
        config={chartConfig}
        xDataKey="time"
        className="mt-4 min-h-0 w-full flex-1"
        curveType="linear"
        enableHoverHighlight
        selectedDataKey={selected}
        onSelectionChange={setSelected}
      >
        <AreaChart.Grid />
        <AreaChart.XAxis
          dataKey="time"
          label="Time (UTC)"
          tickFormatter={(value) => value.replace("Race ", "")}
        />
        <AreaChart.YAxis />
        <AreaChart.Tooltip />
        <AreaChart.Area dataKey="p50" variant="gradient" strokeVariant="solid" isClickable />
        <AreaChart.Area dataKey="p75" variant="gradient" strokeVariant="solid" isClickable />
        <AreaChart.Area dataKey="p95" variant="gradient" strokeVariant="solid" isClickable />
        <AreaChart.Area dataKey="p99" variant="gradient" strokeVariant="solid" isClickable />
      </AreaChart>
    </div>
  );
}

```

## Endowment fund comparison [#endowment-fund-comparison]

Use this two-series layout when direction and change over the same date range matter. Replace the date and both fund fields, then update the summary values above the plot. Keep gains and losses understandable without color alone.

### Endowment fund comparison

```tsx
"use client";

import { AreaChart, type ChartConfig } from "honestui/charts";

// Scenario: College endowment funds
const SERIES = [
  { key: "robinhood", label: "Sustainable fund", color: "#c3f000", pct: -4.41, delta: -2377.66 },
  { key: "coinbase", label: "Bond fund", color: "#2f6bff", pct: 1.15, delta: 617.22 },
] as const;

const chartData = [
  { date: "Dec 22", robinhood: 59847, coinbase: 59574 },
  { date: "Dec 23", robinhood: 60362, coinbase: 58919 },
  { date: "Dec 24", robinhood: 60784, coinbase: 58231 },
  { date: "Dec 25", robinhood: 60473, coinbase: 57587 },
  { date: "Dec 26", robinhood: 60007, coinbase: 56987 },
  { date: "Dec 27", robinhood: 59740, coinbase: 56499 },
  { date: "Dec 28", robinhood: 59407, coinbase: 56077 },
  { date: "Dec 29", robinhood: 59096, coinbase: 55722 },
  { date: "Dec 30", robinhood: 58919, coinbase: 55456 },
  { date: "Dec 31", robinhood: 58652, coinbase: 55234 },
  { date: "Jan 1", robinhood: 58497, coinbase: 55100 },
  { date: "Jan 2", robinhood: 58297, coinbase: 55056 },
  { date: "Jan 3", robinhood: 58186, coinbase: 55189 },
  { date: "Jan 4", robinhood: 58097, coinbase: 55478 },
  { date: "Jan 5", robinhood: 57986, coinbase: 55900 },
  { date: "Jan 6", robinhood: 57875, coinbase: 56410 },
  { date: "Jan 7", robinhood: 57809, coinbase: 56987 },
  { date: "Jan 8", robinhood: 57742, coinbase: 57609 },
  { date: "Jan 9", robinhood: 57676, coinbase: 58208 },
  { date: "Jan 10", robinhood: 57609, coinbase: 58786 },
  { date: "Jan 11", robinhood: 57565, coinbase: 59296 },
  { date: "Jan 12", robinhood: 57498, coinbase: 59696 },
  { date: "Jan 13", robinhood: 57431, coinbase: 59984 },
  { date: "Jan 14", robinhood: 57387, coinbase: 60162 },
  { date: "Jan 15", robinhood: 57320, coinbase: 60273 },
  { date: "Jan 16", robinhood: 57254, coinbase: 60295 },
  { date: "Jan 17", robinhood: 57207, coinbase: 60259 },
];

const chartConfig = {
  robinhood: { label: "Sustainable fund", colors: { light: ["#a6cc00"], dark: ["#c3f000"] } },
  coinbase: { label: "Bond fund", colors: { light: ["#2f6bff"], dark: ["#4c86ff"] } },
} satisfies ChartConfig;

const money = (value: number) =>
  Math.abs(value).toLocaleString("en-US", { minimumFractionDigits: 2, maximumFractionDigits: 2 });

export function PortfolioAreaChart() {
  return (
    <div className="flex h-full w-full flex-col pt-4">
      <div className="grid grid-cols-2 gap-x-8 px-4">
        {SERIES.map(({ key, label, color, pct, delta }) => (
          <div key={key} className="flex flex-col gap-1">
            <div className="text-muted-foreground flex items-center gap-2 text-sm">
              <span
                className="size-3 shrink-0 rounded-full border-2"
                style={{ borderColor: color }}
              />
              {label}
            </div>
            <div className="text-primary text-2xl font-semibold tracking-tight sm:text-3xl">
              {pct > 0 ? "+" : "−"}
              {Math.abs(pct).toFixed(2)}%
            </div>
            <div className={delta < 0 ? "text-rose-500" : "text-emerald-500"}>
              {delta < 0 ? "−" : "+"}${money(delta)}
            </div>
          </div>
        ))}
      </div>

      <AreaChart
        data={chartData}
        config={chartConfig}
        xDataKey="date"
        className="mt-4 min-h-0 w-full flex-1"
        curveType="step"
        enableHoverReveal
        chartOptions={{
          grid: { left: 0, right: 0, top: 16, bottom: 0 },
          yAxis: { type: "value", show: false, scale: true, boundaryGap: ["12%", "16%"] },
        }}
      >
        <AreaChart.Tooltip variant="frosted-glass" />
        <AreaChart.Area dataKey="robinhood" variant="dotted" strokeVariant="solid">
          <AreaChart.ActiveDot variant="ping" />
        </AreaChart.Area>
        <AreaChart.Area dataKey="coinbase" variant="dotted" strokeVariant="solid">
          <AreaChart.ActiveDot variant="ping" />
        </AreaChart.Area>
      </AreaChart>
    </div>
  );
}

```

## Tree planting goal [#tree-planting-goal]

Use this layout to compare an observed value with a target. Replace `date`, `actual`, and `target`, and state the latest difference in text. The solid and dashed strokes reinforce the series distinction without relying only on color.

### Tree planting goal

```tsx
"use client";

import { AreaChart, type ChartConfig } from "honestui/charts";

// Scenario: Urban tree planting
const chartData = [
  { date: "Jan 1", actual: 266, target: 125 },
  { date: "Jan 8", actual: 266, target: 125 },
  { date: "Jan 15", actual: 194, target: 200 },
  { date: "Jan 22", actual: 194, target: 200 },
  { date: "Feb 1", actual: 194, target: 200 },
  { date: "Feb 8", actual: 289, target: 125 },
  { date: "Feb 15", actual: 289, target: 125 },
  { date: "Feb 22", actual: 289, target: 125 },
  { date: "Mar 1", actual: 289, target: 190 },
  { date: "Mar 8", actual: 372, target: 190 },
  { date: "Mar 15", actual: 372, target: 190 },
  { date: "Mar 22", actual: 372, target: 390 },
  { date: "Apr 1", actual: 372, target: 390 },
  { date: "Apr 8", actual: 372, target: 390 },
  { date: "Apr 15", actual: 211, target: 390 },
  { date: "Apr 22", actual: 239, target: 305 },
  { date: "May 1", actual: 239, target: 305 },
  { date: "May 8", actual: 239, target: 175 },
  { date: "May 15", actual: 239, target: 175 },
  { date: "May 22", actual: 305, target: 175 },
  { date: "Jun 1", actual: 305, target: 245 },
];

const chartConfig = {
  actual: { label: "Planted", colors: { light: ["#0a0a0a"], dark: ["#ffffff"] } },
  target: { label: "Season goal", colors: { light: ["#a1a1aa"], dark: ["#666"] } },
} satisfies ChartConfig;

const LATEST = chartData[chartData.length - 1];
const DELTA = ((LATEST.actual - LATEST.target) / LATEST.target) * 100;

export function BenchmarkAreaChart() {
  return (
    <div className="flex h-full w-full flex-col p-4">
      <div className="flex items-start justify-between gap-4">
        <div className="flex flex-col gap-1">
          <span className="text-muted-foreground text-xs">Saplings planted</span>
          <div className="flex items-baseline gap-2">
            <span className="text-primary text-2xl font-semibold tracking-tight sm:text-3xl">
              {LATEST.actual}
            </span>
            <span className="text-sm font-medium text-emerald-500">
              +{DELTA.toFixed(1)}% vs target
            </span>
          </div>
        </div>

        <div className="flex flex-col items-end gap-1.5 pt-1">
          <div className="text-muted-foreground flex items-center gap-2 text-xs">
            <svg className="text-primary" width="18" height="2" viewBox="0 0 18 2" aria-hidden>
              <line x1="0" y1="1" x2="18" y2="1" stroke="currentColor" strokeWidth="2" />
            </svg>
            Actual
          </div>
          <div className="text-muted-foreground flex items-center gap-2 text-xs">
            <svg width="18" height="2" viewBox="0 0 18 2" aria-hidden>
              <line
                x1="0"
                y1="1"
                x2="18"
                y2="1"
                stroke="currentColor"
                strokeWidth="2"
                strokeDasharray="4 3"
              />
            </svg>
            Target
          </div>
        </div>
      </div>

      <AreaChart
        data={chartData}
        config={chartConfig}
        xDataKey="date"
        className="mt-4 min-h-0 w-full flex-1"
        curveType="smooth"
      >
        <AreaChart.Grid />
        <AreaChart.XAxis
          dataKey="date"
          tickFormatter={(value) => (value.split(" ")[1] === "1" ? value.split(" ")[0] : "")}
        />
        <AreaChart.YAxis />
        <AreaChart.Tooltip />
        <AreaChart.Area dataKey="target" variant="none" strokeVariant="dashed" />
        <AreaChart.Area dataKey="actual" variant="lines" strokeVariant="solid" />
      </AreaChart>
    </div>
  );
}

```

## Bike rental demand [#bike-rental-demand]

Use this single-series layout for a compact trend with a visible total. Replace `month` and `rentals`, then update the heading, unit, and aggregation. Add a summary of the direction or highest period when that conclusion matters.

### Bike rental demand

```tsx
"use client";

import { AreaChart, type ChartConfig } from "honestui/charts";

// Scenario: Bike rental demand
const chartData = [
  { month: "Jan", rentals: 3308 },
  { month: "Feb", rentals: 3463 },
  { month: "Mar", rentals: 3841 },
  { month: "Apr", rentals: 3752 },
  { month: "May", rentals: 4129 },
  { month: "Jun", rentals: 4640 },
  { month: "Jul", rentals: 5062 },
];

const chartConfig = {
  rentals: {
    label: "Bike rentals",
    colors: {
      light: ["#10b981", "#0ea5e9", "#8b5cf6"],
      dark: ["#34d399", "#38bdf8", "#a78bfa"],
    },
  },
} satisfies ChartConfig;

const TOTAL = chartData.reduce((sum, { rentals }) => sum + rentals, 0);

export function AudienceAreaChart() {
  return (
    <div className="flex h-full w-full flex-col">
      <div className="flex items-start justify-between gap-4 px-4 pt-4">
        <div className="flex flex-col gap-1">
          <span className="text-primary text-base font-medium tracking-tight sm:text-lg">
            Bike rentals
          </span>
          <span className="text-muted-foreground max-w-[26ch] text-xs leading-snug">
            Monthly trips across all docking stations
          </span>
        </div>
        <div className="flex shrink-0 flex-col items-end gap-2">
          <span className="text-primary text-2xl font-semibold tracking-tight sm:text-4xl">
            {TOTAL.toLocaleString("en-US")}
          </span>
          <span className="text-muted-foreground text-xs">Total rentals</span>
        </div>
      </div>

      <div className="relative mt-2 min-h-0 w-full flex-1">
        <AreaChart
          data={chartData}
          config={chartConfig}
          xDataKey="month"
          className="h-full w-full"
          curveType="monotone"
          chartOptions={{
            grid: { left: 0, right: 0, top: 16, bottom: 0, outerBoundsMode: "none" },
            yAxis: { type: "value", show: false, scale: true, boundaryGap: ["16%", "20%"] },
          }}
        >
          <AreaChart.Tooltip variant="frosted-glass" />
          <AreaChart.Area
            dataKey="rentals"
            variant="gradient"
            strokeVariant="solid"
            strokeWidth={2.5}
          >
            <AreaChart.ActiveDot variant="ping" />
          </AreaChart.Area>
        </AreaChart>

        <div className="text-muted-foreground pointer-events-none absolute inset-x-0 bottom-0 flex justify-between px-4 pb-3 text-[10px] sm:text-xs">
          {chartData.map(({ month }) => (
            <span key={month}>{month}</span>
          ))}
        </div>
      </div>
    </div>
  );
}

```
