---
title: Area Blocks
description: Ready-made area chart blocks, powered by Apache ECharts
image: /og/og-image.png
---

## Latency Percentiles

### Latency Percentiles

```tsx
"use client";

import { EChartsAreaChart, type ChartConfig } from "@/components/evilcharts/charts/echarts-area-chart";
import { cn } from "@/lib/utils";
import { useState } from "react";

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: "Today 13:06", p99: 188, p95: 92, p75: 44, p50: 20 },
  { time: "Today 13:07", p99: 196, p95: 95, p75: 46, p50: 21 },
  { time: "Today 13:08", p99: 181, p95: 89, p75: 43, p50: 20 },
  { time: "Today 13:09", p99: 192, p95: 94, p75: 47, p50: 22 },
  { time: "Today 13:10", p99: 205, p95: 99, p75: 45, p50: 21 },
  { time: "Today 13:11", p99: 187, p95: 91, p75: 44, p50: 20 },
  { time: "Today 13:12", p99: 179, p95: 88, p75: 42, p50: 19 },
  { time: "Today 13:13", p99: 198, p95: 96, p75: 46, p50: 21 },
  { time: "Today 13:14", p99: 210, p95: 101, p75: 48, p50: 22 },
  { time: "Today 13:15", p99: 194, p95: 93, p75: 45, p50: 21 },
  { time: "Today 13:16", p99: 202, p95: 97, p75: 47, p50: 22 },
  { time: "Today 13:17", p99: 215, p95: 104, p75: 49, p50: 23 },
  { time: "Today 13:18", p99: 231, p95: 112, p75: 52, p50: 23 },
  { time: "Today 13:19", p99: 278, p95: 131, p75: 57, p50: 24 },
  { time: "Today 13:20", p99: 306, p95: 142, p75: 61, p50: 25 },
  { time: "Today 13:21", p99: 289, p95: 135, p75: 58, p50: 24 },
  { time: "Today 13:22", p99: 247, p95: 118, p75: 53, p50: 23 },
  { time: "Today 13:23", p99: 216, p95: 105, p75: 49, p50: 22 },
  { time: "Today 13:24", p99: 201, p95: 97, p75: 46, p50: 21 },
  { time: "Today 13:25", p99: 193, p95: 94, p75: 45, p50: 21 },
  { time: "Today 13:26", p99: 186, p95: 90, p75: 44, p50: 20 },
  { time: "Today 13:27", p99: 199, p95: 96, p75: 46, p50: 21 },
  { time: "Today 13:28", p99: 207, p95: 100, p75: 47, p50: 22 },
  { time: "Today 13:29", p99: 191, p95: 92, p75: 45, p50: 20 },
  { time: "Today 13:30", p99: 184, p95: 89, p75: 43, p50: 20 },
  { time: "Today 13:31", p99: 196, p95: 95, p75: 46, p50: 21 },
  { time: "Today 13:32", p99: 209, p95: 101, p75: 48, p50: 22 },
  { time: "Today 13:33", p99: 198, p95: 96, p75: 46, p50: 21 },
  { time: "Today 13:34", p99: 204, p95: 98, p75: 46, p50: 21 },
];

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 EChartsLatencyAreaChart() {
  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>
      <EChartsAreaChart
        data={chartData}
        config={chartConfig}
        xDataKey="time"
        className="mt-4 min-h-0 w-full flex-1"
        curveType="linear"
        enableHoverHighlight
        selectedDataKey={selected}
        onSelectionChange={setSelected}
      >
        <EChartsAreaChart.Grid />
        <EChartsAreaChart.XAxis
          dataKey="time"
          label="Time (UTC)"
          tickFormatter={(value) => value.replace("Today ", "")}
        />
        <EChartsAreaChart.YAxis />
        <EChartsAreaChart.Tooltip />
        <EChartsAreaChart.Area dataKey="p50" variant="gradient" strokeVariant="solid" isClickable />
        <EChartsAreaChart.Area dataKey="p75" variant="gradient" strokeVariant="solid" isClickable />
        <EChartsAreaChart.Area dataKey="p95" variant="gradient" strokeVariant="solid" isClickable />
        <EChartsAreaChart.Area dataKey="p99" variant="gradient" strokeVariant="solid" isClickable />
      </EChartsAreaChart>
    </div>
  );
}

```
### npm

```bash
npx shadcn@latest add @evilcharts/latency-echarts-area-chart
```

### yarn

```bash
yarn shadcn@latest add @evilcharts/latency-echarts-area-chart
```

### bun

```bash
bunx --bun shadcn@latest add @evilcharts/latency-echarts-area-chart
```

### pnpm

```bash
pnpm dlx shadcn@latest add @evilcharts/latency-echarts-area-chart
```

## Portfolio Comparison

### Portfolio Comparison

```tsx
"use client";

import { EChartsAreaChart, type ChartConfig } from "@/components/evilcharts/charts/echarts-area-chart";

const SERIES = [
  { key: "robinhood", label: "Robinhood", color: "#c3f000", pct: -4.41, delta: -2377.66 },
  { key: "coinbase", label: "Coinbase", color: "#2f6bff", pct: 1.15, delta: 617.22 },
] as const;

const chartData = [
  { date: "Dec 22", robinhood: 53916, coinbase: 53670 },
  { date: "Dec 23", robinhood: 54380, coinbase: 53080 },
  { date: "Dec 24", robinhood: 54760, coinbase: 52460 },
  { date: "Dec 25", robinhood: 54480, coinbase: 51880 },
  { date: "Dec 26", robinhood: 54060, coinbase: 51340 },
  { date: "Dec 27", robinhood: 53820, coinbase: 50900 },
  { date: "Dec 28", robinhood: 53520, coinbase: 50520 },
  { date: "Dec 29", robinhood: 53240, coinbase: 50200 },
  { date: "Dec 30", robinhood: 53080, coinbase: 49960 },
  { date: "Dec 31", robinhood: 52840, coinbase: 49760 },
  { date: "Jan 1", robinhood: 52700, coinbase: 49640 },
  { date: "Jan 2", robinhood: 52520, coinbase: 49600 },
  { date: "Jan 3", robinhood: 52420, coinbase: 49720 },
  { date: "Jan 4", robinhood: 52340, coinbase: 49980 },
  { date: "Jan 5", robinhood: 52240, coinbase: 50360 },
  { date: "Jan 6", robinhood: 52140, coinbase: 50820 },
  { date: "Jan 7", robinhood: 52080, coinbase: 51340 },
  { date: "Jan 8", robinhood: 52020, coinbase: 51900 },
  { date: "Jan 9", robinhood: 51960, coinbase: 52440 },
  { date: "Jan 10", robinhood: 51900, coinbase: 52960 },
  { date: "Jan 11", robinhood: 51860, coinbase: 53420 },
  { date: "Jan 12", robinhood: 51800, coinbase: 53780 },
  { date: "Jan 13", robinhood: 51740, coinbase: 54040 },
  { date: "Jan 14", robinhood: 51700, coinbase: 54200 },
  { date: "Jan 15", robinhood: 51640, coinbase: 54300 },
  { date: "Jan 16", robinhood: 51580, coinbase: 54320 },
  { date: "Jan 17", robinhood: 51538, coinbase: 54287 },
];

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

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

export function EChartsPortfolioAreaChart() {
  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>

      <EChartsAreaChart
        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%"] },
        }}
      >
        <EChartsAreaChart.Tooltip variant="frosted-glass" />
        <EChartsAreaChart.Area dataKey="robinhood" variant="dotted" strokeVariant="solid">
          <EChartsAreaChart.ActiveDot variant="ping" />
        </EChartsAreaChart.Area>
        <EChartsAreaChart.Area dataKey="coinbase" variant="dotted" strokeVariant="solid">
          <EChartsAreaChart.ActiveDot variant="ping" />
        </EChartsAreaChart.Area>
      </EChartsAreaChart>
    </div>
  );
}

```
### npm

```bash
npx shadcn@latest add @evilcharts/portfolio-echarts-area-chart
```

### yarn

```bash
yarn shadcn@latest add @evilcharts/portfolio-echarts-area-chart
```

### bun

```bash
bunx --bun shadcn@latest add @evilcharts/portfolio-echarts-area-chart
```

### pnpm

```bash
pnpm dlx shadcn@latest add @evilcharts/portfolio-echarts-area-chart
```

## Growth Benchmark

### Growth Benchmark

```tsx
"use client";

import { EChartsAreaChart, type ChartConfig } from "@/components/evilcharts/charts/echarts-area-chart";

const chartData = [
  { date: "Jan 1", actual: 240, target: 125 },
  { date: "Jan 8", actual: 240, target: 125 },
  { date: "Jan 15", actual: 175, target: 200 },
  { date: "Jan 22", actual: 175, target: 200 },
  { date: "Feb 1", actual: 175, target: 200 },
  { date: "Feb 8", actual: 260, target: 125 },
  { date: "Feb 15", actual: 260, target: 125 },
  { date: "Feb 22", actual: 260, target: 125 },
  { date: "Mar 1", actual: 260, target: 190 },
  { date: "Mar 8", actual: 335, target: 190 },
  { date: "Mar 15", actual: 335, target: 190 },
  { date: "Mar 22", actual: 335, target: 390 },
  { date: "Apr 1", actual: 335, target: 390 },
  { date: "Apr 8", actual: 335, target: 390 },
  { date: "Apr 15", actual: 190, target: 390 },
  { date: "Apr 22", actual: 215, target: 305 },
  { date: "May 1", actual: 215, target: 305 },
  { date: "May 8", actual: 215, target: 175 },
  { date: "May 15", actual: 215, target: 175 },
  { date: "May 22", actual: 275, target: 175 },
  { date: "Jun 1", actual: 275, target: 245 },
];

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

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

export function EChartsBenchmarkAreaChart() {
  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">Weekly signups</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>

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

```
### npm

```bash
npx shadcn@latest add @evilcharts/benchmark-echarts-area-chart
```

### yarn

```bash
yarn shadcn@latest add @evilcharts/benchmark-echarts-area-chart
```

### bun

```bash
bunx --bun shadcn@latest add @evilcharts/benchmark-echarts-area-chart
```

### pnpm

```bash
pnpm dlx shadcn@latest add @evilcharts/benchmark-echarts-area-chart
```

## Audience Growth

### Audience Growth

```tsx
"use client";

import { EChartsAreaChart, type ChartConfig } from "@/components/evilcharts/charts/echarts-area-chart";

const chartData = [
  { month: "Jan", listeners: 2980 },
  { month: "Feb", listeners: 3120 },
  { month: "Mar", listeners: 3460 },
  { month: "Apr", listeners: 3380 },
  { month: "May", listeners: 3720 },
  { month: "Jun", listeners: 4180 },
  { month: "Jul", listeners: 4560 },
];

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

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

export function EChartsAudienceAreaChart() {
  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">
            Listeners
          </span>
          <span className="text-muted-foreground max-w-[26ch] text-xs leading-snug">
            Monthly reach across every show and episode
          </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 listeners</span>
        </div>
      </div>

      <div className="relative mt-2 min-h-0 w-full flex-1">
        <EChartsAreaChart
          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%"] },
          }}
        >
          <EChartsAreaChart.Tooltip variant="frosted-glass" />
          <EChartsAreaChart.Area
            dataKey="listeners"
            variant="gradient"
            strokeVariant="solid"
            strokeWidth={2.5}
          >
            <EChartsAreaChart.ActiveDot variant="ping" />
          </EChartsAreaChart.Area>
        </EChartsAreaChart>

        <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>
  );
}

```
### npm

```bash
npx shadcn@latest add @evilcharts/audience-echarts-area-chart
```

### yarn

```bash
yarn shadcn@latest add @evilcharts/audience-echarts-area-chart
```

### bun

```bash
bunx --bun shadcn@latest add @evilcharts/audience-echarts-area-chart
```

### pnpm

```bash
pnpm dlx shadcn@latest add @evilcharts/audience-echarts-area-chart
```
