# Bar Chart Stacked

Two series stacked into one bar per month, with a legend below the plot.

Bar Chart Stacked is an open-source chart from suiss/ui for React, React Native, SwiftUI and Jetpack Compose. The @esuiss/ui CLI copies its source into your project, so you own the code.

Source: https://ui.suiss.app/docs/charts/bar-chart-stacked

**React**

**Example**

```tsx title="components/charts/bar-chart-stacked.tsx"
"use client"

import { Bar, BarChart, CartesianGrid, XAxis } from "recharts"

import {
  Card,
  CardContent,
  CardDescription,
  CardHeader,
  CardTitle,
} from "@/components/ui/card"
import {
  type ChartConfig,
  ChartContainer,
  ChartLegend,
  ChartLegendContent,
  ChartTooltip,
  ChartTooltipContent,
} from "@/components/ui/chart"

const chartData = [
  { month: "January", desktop: 186, mobile: 80 },
  { month: "February", desktop: 305, mobile: 200 },
  { month: "March", desktop: 237, mobile: 120 },
  { month: "April", desktop: 173, mobile: 190 },
  { month: "May", desktop: 209, mobile: 130 },
  { month: "June", desktop: 264, mobile: 140 },
]

const chartConfig = {
  desktop: { label: "Desktop", color: "var(--chart-1)" },
  mobile: { label: "Mobile", color: "var(--chart-2)" },
} satisfies ChartConfig

export function BarChartStacked() {
  return (
    <Card variant="filled" size="sm">
      <CardHeader>
        <CardTitle>Stacked</CardTitle>
        <CardDescription>With a legend</CardDescription>
      </CardHeader>
      <CardContent>
        <ChartContainer
          config={chartConfig}
          className="aspect-auto h-56 w-full"
        >
          <BarChart
            accessibilityLayer
            data={chartData}
            margin={{ top: 10, right: 4, left: 4, bottom: 0 }}
          >
            <CartesianGrid vertical={false} strokeDasharray="2 4" />
            <XAxis
              dataKey="month"
              tickLine={false}
              axisLine={false}
              tickMargin={8}
              interval={0}
              tickFormatter={(value: string) => value.slice(0, 3)}
            />
            <ChartTooltip
              cursor={false}
              content={<ChartTooltipContent hideLabel />}
            />
            <ChartLegend content={<ChartLegendContent />} />
            <Bar
              dataKey="desktop"
              stackId="a"
              fill="var(--color-desktop)"
              radius={[0, 0, 4, 4]}
              maxBarSize={36}
            />
            <Bar
              dataKey="mobile"
              stackId="a"
              fill="var(--color-mobile)"
              radius={[4, 4, 0, 0]}
              maxBarSize={36}
            />
          </BarChart>
        </ChartContainer>
      </CardContent>
    </Card>
  )
}
```

Two series stacked into one bar per month, with a legend under the plot. Use it when the total per month matters as well as how each series contributes to it.

## Installation

### CLI

```bash title="Terminal"
npx shadcn@latest add @esuiss/bar-chart-stacked
```

Works in any project with a `components.json`. See [Installation](https://ui.suiss.app/docs/installation#add-components) for the one-line registry setup. Or use the suiss/ui CLI:

```bash title="Terminal"
npx @esuiss/ui@latest add charts bar-chart-stacked
```

### Manual

1. Install the dependencies:

```bash title="Terminal"
npm install recharts
```

2. Add the pieces it builds on: [Card](https://ui.suiss.app/docs/components/card), [Chart](https://ui.suiss.app/docs/components/chart).

3. Copy the source into `components/charts/bar-chart-stacked.tsx`:

```tsx title="components/charts/bar-chart-stacked.tsx"
"use client"

import { Bar, BarChart, CartesianGrid, XAxis } from "recharts"

import {
  Card,
  CardContent,
  CardDescription,
  CardHeader,
  CardTitle,
} from "@/components/ui/card"
import {
  type ChartConfig,
  ChartContainer,
  ChartLegend,
  ChartLegendContent,
  ChartTooltip,
  ChartTooltipContent,
} from "@/components/ui/chart"

const chartData = [
  { month: "January", desktop: 186, mobile: 80 },
  { month: "February", desktop: 305, mobile: 200 },
  { month: "March", desktop: 237, mobile: 120 },
  { month: "April", desktop: 173, mobile: 190 },
  { month: "May", desktop: 209, mobile: 130 },
  { month: "June", desktop: 264, mobile: 140 },
]

const chartConfig = {
  desktop: { label: "Desktop", color: "var(--chart-1)" },
  mobile: { label: "Mobile", color: "var(--chart-2)" },
} satisfies ChartConfig

export function BarChartStacked() {
  return (
    <Card variant="filled" size="sm">
      <CardHeader>
        <CardTitle>Stacked</CardTitle>
        <CardDescription>With a legend</CardDescription>
      </CardHeader>
      <CardContent>
        <ChartContainer
          config={chartConfig}
          className="aspect-auto h-56 w-full"
        >
          <BarChart
            accessibilityLayer
            data={chartData}
            margin={{ top: 10, right: 4, left: 4, bottom: 0 }}
          >
            <CartesianGrid vertical={false} strokeDasharray="2 4" />
            <XAxis
              dataKey="month"
              tickLine={false}
              axisLine={false}
              tickMargin={8}
              interval={0}
              tickFormatter={(value: string) => value.slice(0, 3)}
            />
            <ChartTooltip
              cursor={false}
              content={<ChartTooltipContent hideLabel />}
            />
            <ChartLegend content={<ChartLegendContent />} />
            <Bar
              dataKey="desktop"
              stackId="a"
              fill="var(--color-desktop)"
              radius={[0, 0, 4, 4]}
              maxBarSize={36}
            />
            <Bar
              dataKey="mobile"
              stackId="a"
              fill="var(--color-mobile)"
              radius={[4, 4, 0, 0]}
              maxBarSize={36}
            />
          </BarChart>
        </ChartContainer>
      </CardContent>
    </Card>
  )
}
```

4. Update the import paths to match your project.

## Usage

```tsx
import { BarChartStacked } from "@/components/charts/bar-chart-stacked"
```

```tsx
<BarChartStacked />
```

Replace `chartData` and rename the series keys in the data, both `Bar` elements and `chartConfig`. Every bar in the stack needs the same `stackId`; the first one sits at the bottom. If you add a series, move the rounded top corners via `radius` to whichever bar is on top.

## Notes

The legend takes labels and colors from the config. Focus the chart and use the arrow keys to move between months.

**React Native, SwiftUI, Jetpack Compose**

![Preview](https://ui.suiss.app/previews/charts-bar--stacked.light.webp)

```tsx title="uniwind/components/charts/bar-chart-stacked.tsx"
import {
  Card,
  CardContent,
  CardDescription,
  CardHeader,
  CardTitle,
} from "@/components/ui/card"
import {
  Bar,
  BarChart,
  CartesianGrid,
  type ChartConfig,
  ChartContainer,
  ChartLegend,
  ChartLegendContent,
  ChartTooltip,
  ChartTooltipContent,
  XAxis,
} from "@/components/ui/chart"

const chartData = [
  { month: "January", desktop: 186, mobile: 80 },
  { month: "February", desktop: 305, mobile: 200 },
  { month: "March", desktop: 237, mobile: 120 },
  { month: "April", desktop: 173, mobile: 190 },
  { month: "May", desktop: 209, mobile: 130 },
  { month: "June", desktop: 264, mobile: 140 },
]

const chartConfig = {
  desktop: { label: "Desktop", color: "chart-1" },
  mobile: { label: "Mobile", color: "chart-2" },
} satisfies ChartConfig

export function BarChartStacked() {
  return (
    <Card variant="filled" size="sm">
      <CardHeader>
        <CardTitle>Stacked</CardTitle>
        <CardDescription>With a legend</CardDescription>
      </CardHeader>
      <CardContent>
        <ChartContainer
          config={chartConfig}
          accessibilityLabel="Stacked"
          className="h-56 w-full"
        >
          <BarChart
            data={chartData}
            margin={{ top: 10, right: 4, left: 4, bottom: 0 }}
          >
            <CartesianGrid vertical={false} strokeDasharray="2 4" />
            <XAxis
              dataKey="month"
              tickMargin={8}
              interval={0}
              tickFormatter={(value: string) => value.slice(0, 3)}
            />
            <ChartTooltip
              cursor={false}
              content={<ChartTooltipContent hideLabel />}
            />
            <ChartLegend content={<ChartLegendContent />} />
            <Bar
              dataKey="desktop"
              stackId="a"
              fill="chart-1"
              radius={[0, 0, 4, 4]}
              maxBarSize={36}
            />
            <Bar
              dataKey="mobile"
              stackId="a"
              fill="chart-2"
              radius={[4, 4, 0, 0]}
              maxBarSize={36}
            />
          </BarChart>
        </ChartContainer>
      </CardContent>
    </Card>
  )
}
```

```swift title="swift/Charts/BarChartStacked.swift"
import Charts
import SwiftUI

private let chartData: [(month: String, desktop: Double, mobile: Double)] = [
  ("January", 186, 80),
  ("February", 305, 200),
  ("March", 237, 120),
  ("April", 173, 190),
  ("May", 209, 130),
  ("June", 264, 140),
]

private let chartConfig: ChartConfig = [
  "desktop": .init("Desktop", color: EsuissColor.chart1),
  "mobile": .init("Mobile", color: EsuissColor.chart2),
]

public struct BarChartStacked: View {
  @State private var selection: Int?

  public init() {}

  public var body: some View {
    Card(.filled, size: .sm) {
      CardHeader("Stacked", description: "With a legend")
      CardContent {
        ChartContainer(config: chartConfig) {
          ChartAnimator(animationDuration: 400) { progress in
            Chart {
              ForEach(chartData.indices, id: \.self) { index in
                let item = chartData[index]
                BarMark(x: .value("Month", item.month), yStart: .value("Start", 0), yEnd: .value("Desktop", item.desktop * progress), width: .fixed(36))
                  .clipShape(UnevenRoundedRectangle(bottomLeadingRadius: 4, bottomTrailingRadius: 4, style: .continuous))
                  .foregroundStyle(chartConfig.color("desktop"))
                BarMark(x: .value("Month", item.month), yStart: .value("Start", item.desktop * progress), yEnd: .value("Mobile", (item.desktop + item.mobile) * progress), width: .fixed(36))
                  .clipShape(UnevenRoundedRectangle(topLeadingRadius: 4, topTrailingRadius: 4, style: .continuous))
                  .foregroundStyle(chartConfig.color("mobile"))
                ChartAnchorMark(x: .value("Month", item.month), y: .value("Mobile", item.desktop + item.mobile))
              }
            }
            .chartXAxis { ChartAxis.x(values: chartData.map(\.month)) { String($0.prefix(3)) } }
            .chartYAxis { ChartAxis.grid() }
            .chartTooltip(
              x: chartData.map(\.month),
              selection: $selection,
              cursor: false,
              accessibilityLabel: "Stacked",
              items: { index in
                [
                  ChartTooltipItem("desktop", value: chartData[index].desktop),
                  ChartTooltipItem("mobile", value: chartData[index].mobile),
                ]
              }
            ) { payload in
              ChartTooltipContent(payload, hideLabel: true)
            }
          }
          ChartLegendContent()
        }
        .frame(height: 224)
      }
    }
  }
}
```

```kt title="kotlin/charts/BarChartStacked.kt"
package com.esuiss.ui.charts

import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.height
import androidx.compose.runtime.Composable
import androidx.compose.ui.Modifier
import androidx.compose.ui.unit.dp
import com.esuiss.ui.components.BarChart
import com.esuiss.ui.components.Card
import com.esuiss.ui.components.CardContent
import com.esuiss.ui.components.CardHeader
import com.esuiss.ui.components.CardSize
import com.esuiss.ui.components.CardVariant
import com.esuiss.ui.components.ChartConfig
import com.esuiss.ui.components.ChartConfigItem
import com.esuiss.ui.components.ChartContainer
import com.esuiss.ui.components.ChartMargin
import com.esuiss.ui.components.ChartRadius
import com.esuiss.ui.components.ChartTooltipContent

private class BarStackedRow(val month: String, val desktop: Double, val mobile: Double)

private val chartData = listOf(
    BarStackedRow("January", 186.0, 80.0),
    BarStackedRow("February", 305.0, 200.0),
    BarStackedRow("March", 237.0, 120.0),
    BarStackedRow("April", 173.0, 190.0),
    BarStackedRow("May", 209.0, 130.0),
    BarStackedRow("June", 264.0, 140.0),
)

private val chartConfig = ChartConfig(
    "desktop" to ChartConfigItem("Desktop") { chart1 },
    "mobile" to ChartConfigItem("Mobile") { chart2 },
)

@Composable
fun BarChartStacked(modifier: Modifier = Modifier) {
    Card(modifier, variant = CardVariant.Filled, size = CardSize.Sm) {
        CardHeader("Stacked", description = "With a legend")
        CardContent {
            ChartContainer(
                chartConfig,
                Modifier
                    .fillMaxWidth()
                    .height(224.dp),
            ) {
                BarChart(
                    chartData,
                    margin = ChartMargin(top = 10.dp, right = 4.dp, bottom = 0.dp, left = 4.dp),
                    accessibilityLabel = "Stacked",
                ) {
                    CartesianGrid(strokeDasharray = listOf(2f, 4f))
                    XAxis(label = { it.month }, tickMargin = 8.dp, interval = 0, tickFormatter = { it.take(3) })
                    ChartTooltip(cursor = null) { ChartTooltipContent(hideLabel = true) }
                    ChartLegend()
                    Bar(
                        "desktop",
                        value = { it.desktop },
                        radius = ChartRadius(0.dp, 0.dp, 4.dp, 4.dp),
                        maxBarSize = 36.dp,
                        stackId = "a",
                    )
                    Bar(
                        "mobile",
                        value = { it.mobile },
                        radius = ChartRadius(4.dp, 4.dp, 0.dp, 0.dp),
                        maxBarSize = 36.dp,
                        stackId = "a",
                    )
                }
            }
        }
    }
}
```

Desktop and mobile bars share a `stackId`, so each column shows both parts and their total. Only the outer corners are rounded, and a legend below the plot names both series.

## Installation

**React Native**

```bash title="Terminal"
npx shadcn@latest add @esuiss-rn/bar-chart-stacked
```

See [Installation](https://ui.suiss.app/docs/installation) for the `components.json` setup. Or use the suiss/ui CLI:

```bash title="Terminal"
npx @esuiss/ui@latest add bar-chart-stacked
```

**SwiftUI**

```bash title="Terminal"
npx @esuiss/ui@latest add bar-chart-stacked --platform swift
```

**Jetpack Compose**

```bash title="Terminal"
npx @esuiss/ui@latest add bar-chart-stacked --platform kotlin
```

The CLI also adds what it builds on: [Card](https://ui.suiss.app/docs/components/card), [Chart](https://ui.suiss.app/docs/components/chart).

### Manual

**React Native**

**Uniwind**

1. Add the pieces it builds on: [Card](https://ui.suiss.app/docs/components/card), [Chart](https://ui.suiss.app/docs/components/chart).

2. Copy the source into `components/charts/bar-chart-stacked.tsx`:

- `uniwind/components/charts/bar-chart-stacked.tsx`: shown above

3. Update the import paths to match your project.

**NativeWind**

1. Add the pieces it builds on: [Card](https://ui.suiss.app/docs/components/card), [Chart](https://ui.suiss.app/docs/components/chart).

2. Copy the source into `components/charts/bar-chart-stacked.tsx`:

```tsx title="nativewind/components/charts/bar-chart-stacked.tsx"
import {
  Card,
  CardContent,
  CardDescription,
  CardHeader,
  CardTitle,
} from "@/components/ui/card"
import {
  Bar,
  BarChart,
  CartesianGrid,
  type ChartConfig,
  ChartContainer,
  ChartLegend,
  ChartLegendContent,
  ChartTooltip,
  ChartTooltipContent,
  XAxis,
} from "@/components/ui/chart"

const chartData = [
  { month: "January", desktop: 186, mobile: 80 },
  { month: "February", desktop: 305, mobile: 200 },
  { month: "March", desktop: 237, mobile: 120 },
  { month: "April", desktop: 173, mobile: 190 },
  { month: "May", desktop: 209, mobile: 130 },
  { month: "June", desktop: 264, mobile: 140 },
]

const chartConfig = {
  desktop: { label: "Desktop", color: "chart-1" },
  mobile: { label: "Mobile", color: "chart-2" },
} satisfies ChartConfig

export function BarChartStacked() {
  return (
    <Card variant="filled" size="sm">
      <CardHeader>
        <CardTitle>Stacked</CardTitle>
        <CardDescription>With a legend</CardDescription>
      </CardHeader>
      <CardContent>
        <ChartContainer
          config={chartConfig}
          accessibilityLabel="Stacked"
          className="h-56 w-full"
        >
          <BarChart
            data={chartData}
            margin={{ top: 10, right: 4, left: 4, bottom: 0 }}
          >
            <CartesianGrid vertical={false} strokeDasharray="2 4" />
            <XAxis
              dataKey="month"
              tickMargin={8}
              interval={0}
              tickFormatter={(value: string) => value.slice(0, 3)}
            />
            <ChartTooltip
              cursor={false}
              content={<ChartTooltipContent hideLabel />}
            />
            <ChartLegend content={<ChartLegendContent />} />
            <Bar
              dataKey="desktop"
              stackId="a"
              fill="chart-1"
              radius={[0, 0, 4, 4]}
              maxBarSize={36}
            />
            <Bar
              dataKey="mobile"
              stackId="a"
              fill="chart-2"
              radius={[4, 4, 0, 0]}
              maxBarSize={36}
            />
          </BarChart>
        </ChartContainer>
      </CardContent>
    </Card>
  )
}
```

3. Update the import paths to match your project.

**StyleSheet**

1. Add the pieces it builds on: [Card](https://ui.suiss.app/docs/components/card), [Chart](https://ui.suiss.app/docs/components/chart).

2. Copy the source into `components/charts/bar-chart-stacked.tsx`:

```tsx title="stylesheet/components/charts/bar-chart-stacked.tsx"
import {
  Card,
  CardContent,
  CardDescription,
  CardHeader,
  CardTitle,
} from "@/components/ui/card"
import {
  Bar,
  BarChart,
  CartesianGrid,
  type ChartConfig,
  ChartContainer,
  ChartLegend,
  ChartLegendContent,
  ChartTooltip,
  ChartTooltipContent,
  XAxis,
} from "@/components/ui/chart"

const chartData = [
  { month: "January", desktop: 186, mobile: 80 },
  { month: "February", desktop: 305, mobile: 200 },
  { month: "March", desktop: 237, mobile: 120 },
  { month: "April", desktop: 173, mobile: 190 },
  { month: "May", desktop: 209, mobile: 130 },
  { month: "June", desktop: 264, mobile: 140 },
]

const chartConfig = {
  desktop: { label: "Desktop", color: "chart-1" },
  mobile: { label: "Mobile", color: "chart-2" },
} satisfies ChartConfig

export function BarChartStacked() {
  return (
    <Card variant="filled" size="sm">
      <CardHeader>
        <CardTitle>Stacked</CardTitle>
        <CardDescription>With a legend</CardDescription>
      </CardHeader>
      <CardContent>
        <ChartContainer
          config={chartConfig}
          accessibilityLabel="Stacked"
          style={{ height: 224 }}
        >
          <BarChart
            data={chartData}
            margin={{ top: 10, right: 4, left: 4, bottom: 0 }}
          >
            <CartesianGrid vertical={false} strokeDasharray="2 4" />
            <XAxis
              dataKey="month"
              tickMargin={8}
              interval={0}
              tickFormatter={(value: string) => value.slice(0, 3)}
            />
            <ChartTooltip
              cursor={false}
              content={<ChartTooltipContent hideLabel />}
            />
            <ChartLegend content={<ChartLegendContent />} />
            <Bar
              dataKey="desktop"
              stackId="a"
              fill="chart-1"
              radius={[0, 0, 4, 4]}
              maxBarSize={36}
            />
            <Bar
              dataKey="mobile"
              stackId="a"
              fill="chart-2"
              radius={[4, 4, 0, 0]}
              maxBarSize={36}
            />
          </BarChart>
        </ChartContainer>
      </CardContent>
    </Card>
  )
}
```

3. Update the import paths to match your project.

**SwiftUI**

1. Target iOS 17 or later.

2. Copy the theme files every view uses:

- `swift/Theme/Theme.swift`: [source](https://github.com/e-suiss/ui/blob/main/packages/mobile/swiftui/Sources/EsuissUI/Theme/Theme.swift)

- `swift/Theme/Tokens.swift`: [source](https://github.com/e-suiss/ui/blob/main/packages/mobile/swiftui/Sources/EsuissUI/Theme/Tokens.swift)

3. Copy the source and every file it uses:

- `swift/Charts/BarChartStacked.swift`: shown above

- `swift/Components/Card.swift`: see [Card](https://ui.suiss.app/docs/components/card.md)

- `swift/Components/Chart.swift`: see [Chart](https://ui.suiss.app/docs/components/chart.md)

- `swift/Components/Haptics.swift`: see [Haptics](https://ui.suiss.app/docs/components/haptics.md)

- `swift/Components/Item.swift`: see [Item](https://ui.suiss.app/docs/components/item.md)

- `swift/Components/Separator.swift`: see [Separator](https://ui.suiss.app/docs/components/separator.md)

- `swift/Components/TextVariant.swift`: [source](https://github.com/e-suiss/ui/blob/main/packages/mobile/swiftui/Sources/EsuissUI/Components/TextVariant.swift)

4. Add the files to your app target.

**Jetpack Compose**

1. Set `minSdk` to 24 or higher and add the dependencies:

```kotlin title="build.gradle.kts"
implementation(platform("androidx.compose:compose-bom:2026.09.00"))
implementation("androidx.compose.foundation:foundation")
implementation("androidx.compose.ui:ui")
```

2. Copy the theme files every view uses:

- `kotlin/theme/Theme.kt`: [source](https://github.com/e-suiss/ui/blob/main/packages/mobile/kotlin/ui/src/main/kotlin/com/esuiss/ui/theme/Theme.kt)

- `kotlin/theme/Tokens.kt`: [source](https://github.com/e-suiss/ui/blob/main/packages/mobile/kotlin/ui/src/main/kotlin/com/esuiss/ui/theme/Tokens.kt)

3. Copy the source and every file it uses:

- `kotlin/charts/BarChartStacked.kt`: shown above

- `kotlin/components/Card.kt`: see [Card](https://ui.suiss.app/docs/components/card.md)

- `kotlin/components/Chart.kt`: see [Chart](https://ui.suiss.app/docs/components/chart.md)

- `kotlin/components/Content.kt`: [source](https://github.com/e-suiss/ui/blob/main/packages/mobile/kotlin/ui/src/main/kotlin/com/esuiss/ui/components/Content.kt)

- `kotlin/components/FormSupport.kt`: [source](https://github.com/e-suiss/ui/blob/main/packages/mobile/kotlin/ui/src/main/kotlin/com/esuiss/ui/components/FormSupport.kt)

- `kotlin/components/Haptics.kt`: see [Haptics](https://ui.suiss.app/docs/components/haptics.md)

- `kotlin/components/Icon.kt`: see [Icon](https://ui.suiss.app/docs/components/icon.md)

4. Update the `com.esuiss.ui` package names to match your project.

## Usage

```tsx
import { BarChartStacked } from "@/components/charts/bar-chart-stacked"
```

```tsx
<BarChartStacked />
```

Replace `chartData` with your records and change each `dataKey` to match your fields. Add, rename or recolor series in `chartConfig` and add a `Bar` for each. The component takes no props.

