# Bar chart

Compare categories with grouped or stacked bars.

## Install

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

## Usage

Use one Bar for each numeric series and match its key to a config entry. Bars share a zero baseline.

Set stacked on Root to add bar series together. Positive and negative values stack on opposite sides of zero. Set orientation="horizontal" when category labels need more room.

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

## Examples

### Grouped series

Compare two measurements side by side for each category.

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

    const data = [
        { month: 'Jan', revenue: 186, target: 160 },
        { month: 'Feb', revenue: 242, target: 190 },
        { month: 'Mar', revenue: 218, target: 220 },
        { month: 'Apr', revenue: 304, target: 250 },
        { month: 'May', revenue: 286, target: 280 },
        { month: 'Jun', revenue: 372, target: 310 }
    ];
    const config = {
        revenue: { label: 'Revenue', color: 'var(--chart-1)' },
        target: { label: 'Target', color: 'var(--chart-2)' }
    };
</script>

<Chart.Root {data} {config} x="month" aria-label="Monthly revenue and target" class="w-full">
    <Chart.Legend />
    <Chart.Plot>
        <Chart.Grid />
        <Chart.XAxis />
        <Chart.YAxis />
        <Chart.Bar key="revenue" />
        <Chart.Bar key="target" />
    </Chart.Plot>
    <Chart.Tooltip />
</Chart.Root>
~~~~

### Horizontal stacks

Stack related counts and keep long category names beside the bars.

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

    const data = [
        { month: 'Jan', desktop: 186, mobile: 160 },
        { month: 'Feb', desktop: 242, mobile: 190 },
        { month: 'Mar', desktop: 218, mobile: 220 },
        { month: 'Apr', desktop: 304, mobile: 250 },
        { month: 'May', desktop: 286, mobile: 280 },
        { month: 'Jun', desktop: 372, mobile: 310 }
    ];
    const config = {
        desktop: { label: 'Desktop', color: 'var(--chart-1)' },
        mobile: { label: 'Mobile', color: 'var(--chart-2)' }
    };
</script>

<Chart.Root
    {data}
    {config}
    x="month"
    aria-label="Monthly visitors by device"
    class="w-full"
    orientation="horizontal"
    stacked
>
    <Chart.Legend />
    <Chart.Plot>
        <Chart.Grid />
        <Chart.XAxis />
        <Chart.YAxis />
        <Chart.Bar key="desktop" />
        <Chart.Bar key="mobile" />
    </Chart.Plot>
    <Chart.Tooltip />
</Chart.Root>
~~~~

### Positive and negative values

Net changes extend on either side of zero. Negative values remain negative in the tooltip.

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

    const data = [
        { month: 'Jan', balance: 24 },
        { month: 'Feb', balance: -12 },
        { month: 'Mar', balance: 38 },
        { month: 'Apr', balance: -6 },
        { month: 'May', balance: 19 }
    ];
    const config = { balance: { label: 'Net new accounts', color: 'var(--chart-1)' } };
</script>

<Chart.Root {data} {config} x="month" aria-label="Net account changes by month" class="w-full">
    <Chart.Plot>
        <Chart.Grid />
        <Chart.XAxis />
        <Chart.YAxis />
        <Chart.Bar key="balance" />
    </Chart.Plot>
    <Chart.Tooltip />
    <Chart.Legend />
</Chart.Root>
~~~~

### Revise a forecast

Switch between forecasts for the same months. Values and the numeric scale transition together, including when you switch again before motion finishes.

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

    let revised = $state(false);
    const data = $derived(
        revised
            ? [
                  { month: 'April', orders: 240 },
                  { month: 'May', orders: 190 },
                  { month: 'June', orders: 320 }
              ]
            : [
                  { month: 'April', orders: 170 },
                  { month: 'May', orders: 280 },
                  { month: 'June', orders: 210 }
              ]
    );
    const config = { orders: { label: 'Orders', color: 'var(--chart-1)' } };

    function switchRevision() {
        revised = !revised;
    }
</script>
<div class="w-full">
    <Button variant="secondary" onclick={switchRevision}>
        {revised ? 'Show original forecast' : 'Show revised forecast'}
    </Button>
    <Chart.Root
        {data}
        {config}
        x="month"
        aria-label="Order forecasts for April through June"
        class="mt-4"
    >
        <Chart.Plot>
            <Chart.Grid />
            <Chart.XAxis />
            <Chart.YAxis />
            <Chart.Bar key="orders" />
        </Chart.Plot>
        <Chart.Tooltip />
        <Chart.Legend />
    </Chart.Root>
</div>
~~~~

### Continuous motion

A light sweep follows the existing bars. It leaves the values unchanged and pauses offscreen.

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

    const data = [
        { month: 'Jan', revenue: 186, target: 160 },
        { month: 'Feb', revenue: 242, target: 190 },
        { month: 'Mar', revenue: 218, target: 220 },
        { month: 'Apr', revenue: 304, target: 250 },
        { month: 'May', revenue: 286, target: 280 },
        { month: 'Jun', revenue: 372, target: 310 }
    ];
    const config = {
        revenue: { label: 'Revenue', color: 'var(--chart-1)' },
        target: { label: 'Target', color: 'var(--chart-2)' }
    };
</script>

<Chart.Root
    {data}
    {config}
    x="month"
    aria-label="Monthly revenue and target"
    class="w-full"
    animation="live"
>
    <Chart.Legend />
    <Chart.Plot>
        <Chart.Grid />
        <Chart.XAxis />
        <Chart.YAxis />
        <Chart.Bar key="revenue" />
        <Chart.Bar key="target" />
    </Chart.Plot>
    <Chart.Tooltip />
</Chart.Root>
~~~~

For the rendered guide, visit [Bar chart](/docs/components/chart/bar).
