# Market share echarts pie chart

> Grayscale donut with a center total and a two-column value legend

A shadcn/ui block from the [@evilcharts](https://evilcharts.com) registry, listed on [shadcn.exchange](https://shadcn.exchange/r/evilcharts/market-share-echarts-pie-chart).

- Page, with a live preview: https://shadcn.exchange/r/evilcharts/market-share-echarts-pie-chart
- Screenshot: https://abzjhwgwpdpz7ua5.public.blob.vercel-storage.com/shots/evilcharts/market-share-echarts-pie-chart-light-7d376cfc051aba8d.png
- Registry item: https://evilcharts.com/r/market-share-echarts-pie-chart.json
- Listed: 2026-10-13, source last changed 2026-10-13

## Install

```bash
npx shadcn@latest add https://evilcharts.com/r/market-share-echarts-pie-chart.json
```

Run it in a project that has shadcn/ui set up. It adds the files below and installs what they depend on.

## Dependencies

- npm packages: `echarts`, `motion`
- Registry items: `@evilcharts/echarts-pie-chart`

## Files

### `src/registry/blocks/echarts/b-market-share-echarts-pie-chart.tsx`

```tsx
"use client";

import { EChartsPieChart, type ChartConfig } from "@/registry/charts/echarts-pie-chart";
import { cn } from "@/lib/utils";
import { useState } from "react";

const SERIES = [
  { key: "skyline", label: "Skyline", value: 27, swatch: "bg-[#0a0a0a] dark:bg-[#ffffff]" },
  { key: "datawell", label: "Datawell", value: 21, swatch: "bg-[#262626] dark:bg-[#dedede]" },
  { key: "cloudpeak", label: "Cloudpeak", value: 13, swatch: "bg-[#3d3d3d] dark:bg-[#bebebe]" },
  { key: "taskbridge", label: "Taskbridge", value: 21, swatch: "bg-[#545454] dark:bg-[#a0a0a0]" },
  { key: "insightloop", label: "Insightloop", value: 6, swatch: "bg-[#6b6b6b] dark:bg-[#868686]" },
  {
    key: "streamforge",
    label: "Streamforge",
    value: 12,
    swatch: "bg-[#7d7d7d] dark:bg-[#6f6f6f]",
  },
] as const;

const chartData = [...SERIES].reverse().map(({ key, value }) => ({
  product: key,
  value,
  share: `${value}%`,
}));

const chartConfig = {
  skyline: { label: "Skyline", colors: { light: ["#0a0a0a"], dark: ["#ffffff"] } },
  datawell: { label: "Datawell", colors: { light: ["#262626"], dark: ["#dedede"] } },
  cloudpeak: { label: "Cloudpeak", colors: { light: ["#3d3d3d"], dark: ["#bebebe"] } },
  taskbridge: { label: "Taskbridge", colors: { light: ["#545454"], dark: ["#a0a0a0"] } },
  insightloop: { label: "Insightloop", colors: { light: ["#6b6b6b"], dark: ["#868686"] } },
  streamforge: { label: "Streamforge", colors: { light: ["#7d7d7d"], dark: ["#6f6f6f"] } },
} satisfies ChartConfig;

const TOTAL = SERIES.reduce((sum, { value }) => sum + value, 0);

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

  return (
    <div className="@container flex h-full w-full flex-col p-4">
      <div className="relative min-h-0 w-full flex-1">
        <EChartsPieChart
          data={chartData}
          config={chartConfig}
          dataKey="value"
          nameKey="product"
          className="h-full w-full"
          selectedSector={selected}
          onSelectionChange={(selection) => setSelected(selection?.dataKey ?? null)}
        >
          <EChartsPieChart.Tooltip />
          <EChartsPieChart.Pie
            isClickable
            innerRadius="52%"
            outerRadius="94%"
            paddingAngle={3}
            startAngle={90}
            endAngle={-270}
          >
            <EChartsPieChart.Label dataKey="share" />
          </EChartsPieChart.Pie>
        </EChartsPieChart>

        <div className="pointer-events-none absolute inset-0 flex flex-col items-center justify-center gap-0.5">
          <span className="text-primary text-xl font-semibold tracking-tight @xl:text-3xl">
            ${TOTAL}B
          </span>
          <span className="text-muted-foreground text-[10px] @xl:text-xs">Ecosystem value</span>
        </div>
      </div>

      <div className="border-border mt-3 grid grid-flow-col grid-cols-2 grid-rows-3 gap-x-6 gap-y-1.5 border-t pt-3">
        {SERIES.map(({ key, label, value, swatch }) => (
          <button
            key={key}
            type="button"
            aria-pressed={selected === key}
            onClick={() => setSelected((prev) => (prev === key ? null : key))}
            className={cn(
              "flex cursor-pointer items-center gap-2 text-left text-xs transition-opacity",
              selected !== null && selected !== key && "opacity-40",
            )}
          >
            <span className={cn("size-3 shrink-0 rounded-[3px]", swatch)} />
            <span className="text-primary font-medium">{label}</span>
            <span className="text-muted-foreground">${value}B</span>
            <span className="text-muted-foreground/60">({value}%)</span>
          </button>
        ))}
      </div>
    </div>
  );
}
```
