# Donut chart

Place a total or active value inside a category breakdown.

## Install

~~~~sh
pnpm dlx @mielui/svelte add pie-chart
~~~~

## Usage

Arc defaults to a donut. Change innerRadius to control the opening and compose Label inside Plot for center content.

Place Legend and Tooltip directly inside Root. Their snippets receive the formatted value and the percentage of the current total.

Use the [Pie Chart API reference](/docs/components/pie-chart.md) for all exported parts and their props.

## Examples

### Total in the center

The default center label shows the total.

~~~~svelte
<script lang="ts">
    import * as PieChart from '@mielui/svelte/components/pie-chart';

    const data = [
        { key: 'direct', value: 1240 },
        { key: 'search', value: 860 },
        { key: 'referral', value: 420 }
    ];
    const config = {
        direct: { label: 'Direct', color: 'var(--chart-1)' },
        search: { label: 'Search', color: 'var(--chart-2)' },
        referral: { label: 'Referral', color: 'var(--chart-3)' }
    };
</script>

<div class="w-full max-w-lg">
    <h3 class="font-medium">Where visitors come from</h3>
    <p class="mt-1 text-sm text-foreground-muted">Sessions by acquisition channel this month.</p>
    <PieChart.Root {data} {config} aria-label="Monthly sessions by acquisition channel">
        <PieChart.Plot class="h-64">
            <PieChart.Arc />
            <PieChart.Label />
        </PieChart.Plot>
        <PieChart.Tooltip />
        <PieChart.Legend />
    </PieChart.Root>
</div>
~~~~

### Switch periods

Switch monthly and annual subscriptions. Slice angles update while the continuous highlight remains decorative.

~~~~svelte
<script lang="ts">
    import { Button } from '@mielui/svelte/components/button';
    import * as PieChart from '@mielui/svelte/components/pie-chart';

    const config = {
        team: { label: 'Team', color: 'var(--chart-1)' },
        solo: { label: 'Solo', color: 'var(--chart-2)' }
    };

    let annual = $state(false);

    const data = $derived([
        { key: 'team', value: annual ? 680 : 420 },
        { key: 'solo', value: annual ? 320 : 580 }
    ]);

    function switchPeriod() {
        annual = !annual;
    }
</script>

<div class="w-full max-w-lg">
    <div class="flex items-center justify-between gap-4">
        <h3 class="font-medium">{annual ? 'Annual plans' : 'Monthly plans'}</h3>
        <Button variant="secondary" onclick={switchPeriod}>Switch period</Button>
    </div>
    <PieChart.Root {data} {config} animation="live" aria-label="Subscriptions by plan">
        <PieChart.Plot>
            <PieChart.Arc innerRadius={0.76} padAngle={0.045} />
            <PieChart.Label>
                {#snippet children({ total, active })}
                    {active ? active.value : total}
                {/snippet}
            </PieChart.Label>
        </PieChart.Plot>
        <PieChart.Tooltip />
        <PieChart.Legend />
    </PieChart.Root>
</div>
~~~~

### Custom center and legend

Replace the center content and legend labels while retaining the shared values and keyboard interaction.

~~~~svelte
<script lang="ts">
    import * as PieChart from '@mielui/svelte/components/pie-chart';

    const data = [
        { key: 'storage', value: 1840 },
        { key: 'compute', value: 3260 },
        { key: 'network', value: 900 }
    ];
    const currency = (value: number) =>
        new Intl.NumberFormat('en-US', {
            style: 'currency',
            currency: 'USD',
            maximumFractionDigits: 0
        }).format(value);
    const config = {
        storage: { label: 'Storage', color: 'var(--chart-1)', format: currency },
        compute: { label: 'Compute', color: 'var(--chart-2)', format: currency },
        network: { label: 'Network', color: 'var(--chart-3)', format: currency }
    };
</script>

<div class="@container w-full max-w-2xl">
    <h3 class="font-medium">Infrastructure spend</h3>
    <p class="mt-1 text-sm text-foreground-muted">This month, before tax.</p>
    <PieChart.Root
        {data}
        {config}
        aria-label="Monthly infrastructure spend"
        class="grid items-center gap-6 @lg:grid-cols-2"
    >
        <PieChart.Plot class="h-64">
            <PieChart.Arc innerRadius={0.78} padAngle={0.025} cornerRadius={3} />
            <PieChart.Label class="text-2xl">
                {#snippet children({ total, active })}
                    <tspan x="0" dy="-0.1em">{currency(active?.value ?? total)}</tspan>
                    <tspan x="0" dy="1.8em" class="fill-foreground-muted text-xs font-normal">
                        {active ? config[active.key as keyof typeof config].label : 'Total spend'}
                    </tspan>
                {/snippet}
            </PieChart.Label>
        </PieChart.Plot>
        <PieChart.Legend
            class="flex-col items-stretch gap-2 [&_button]:w-full [&_button]:justify-between"
        >
            {#snippet children({ item, label, value, percentage })}
                <span class="flex items-center gap-2">
                    <span
                        class="size-2 rounded-full"
                        style:background={config[item.key as keyof typeof config].color}
                    ></span>
                    {label}
                </span>
                <span class="ml-auto pl-4 text-right">
                    <span class="block text-foreground tabular-nums">{value}</span>
                    <span class="block text-xs tabular-nums">{percentage.toFixed(1)}%</span>
                </span>
            {/snippet}
        </PieChart.Legend>
        <PieChart.Tooltip>
            {#snippet children({ label, value, percentage })}
                <span class="grid gap-1">
                    <span class="text-foreground-muted">{label}</span>
                    <span class="font-medium">{`${value} · ${percentage.toFixed(1)}%`}</span>
                </span>
            {/snippet}
        </PieChart.Tooltip>
    </PieChart.Root>
</div>
~~~~

For the rendered guide, visit [Donut chart](/docs/components/pie-chart/donut).
