---
title: BadgeGroup
description: A composable component for displaying a group of badges with overflow handling and "+N more" pattern.
icon: tags
author:
  name: eblairmckee
  url: https://github.com/eblairmckee
mode: wide
---

### Installation

```package-install
npx shadcn@latest add @redpanda/badge-group
```

<ComponentPreview name="badge-group-demo" />

## Usage

The BadgeGroup component displays an explicit collection of items with automatic overflow handling. When there are more items than the `maxVisible` limit, excess items are collapsed into a "+N" badge. Pass a `renderOverflowContent` function to enable a tooltip on the overflow badge.

```tsx
import { Badge } from '@/components/redpanda-ui/badge';
import { BadgeGroup } from '@/components/redpanda-ui/badge-group';
```

Each item requires a unique, nonempty domain `id`, its rendered `content`, and a nonempty plain-text `textValue`. IDs identify items across deletion and reordering; mutable labels and array positions are not identity. JSX children are no longer accepted. Extra domain fields are inferred and preserved in the typed overflow callback.

Hidden items also have a permanent screen-reader description built from `textValue`; the overflow button references that description before the visual tooltip opens. `renderOverflowContent` remains supplementary and is rendered only once. Use wrapping or a visible detail surface when every item must be visually available on touch devices.

## Basic Usage

```tsx
// Shows: [React] [TypeScript] [Tailwind CSS] [+2]
<BadgeGroup
  maxVisible={3}
  items={[
    {
      id: 'React',
      textValue: 'React',
      content: <Badge>React</Badge>,
    },
    {
      id: 'TypeScript',
      textValue: 'TypeScript',
      content: <Badge>TypeScript</Badge>,
    },
    {
      id: 'Tailwind CSS',
      textValue: 'Tailwind CSS',
      content: <Badge>Tailwind CSS</Badge>,
    },
    {
      id: 'Next.js',
      textValue: 'Next.js',
      content: <Badge>Next.js</Badge>,
    },
    {
      id: 'Node.js',
      textValue: 'Node.js',
      content: <Badge>Node.js</Badge>,
    },
  ]}
/>
```

## Props

| Prop                    | Type                                       | Default    | Description                                                          |
| ----------------------- | ------------------------------------------ | ---------- | -------------------------------------------------------------------- |
| `items`                 | `readonly BadgeGroupItem[]`                | —          | Items with stable `id`, `content`, and plain-text `textValue`        |
| `maxVisible`            | `number`                                   | `3`        | Nonnegative integer maximum of visible items                         |
| `size`                  | `'sm' \| 'md' \| 'lg'`                     | `'sm'`     | Size of the overflow badge                                           |
| `tone`                  | `BadgeTone`                                | -          | Semantic color (tone) for the "+N" overflow badge                    |
| `variant`               | `BadgeEmphasis`                            | `'subtle'` | Emphasis for the "+N" overflow badge                                 |
| `gap`                   | `'none' \| 'xs' \| 'sm' \| 'md' \| 'lg'`   | `'sm'`     | Gap between badges                                                   |
| `wrap`                  | `boolean`                                  | `false`    | Allow badges to wrap to multiple lines                               |
| `renderOverflowContent` | `(overflow: readonly Item[]) => ReactNode` | -          | Custom render for overflow tooltip. If omitted, no tooltip is shown. |

## Examples

### With Different Variants

```tsx
<BadgeGroup
  maxVisible={4}
  items={[
    {
      id: 'Active',
      textValue: 'Active',
      content: (
        <Badge tone="success" variant="subtle">
          Active
        </Badge>
      ),
    },
    {
      id: 'Pending',
      textValue: 'Pending',
      content: (
        <Badge tone="warning" variant="subtle">
          Pending
        </Badge>
      ),
    },
    {
      id: 'In Review',
      textValue: 'In Review',
      content: (
        <Badge tone="informative" variant="subtle">
          In Review
        </Badge>
      ),
    },
    {
      id: 'Archived',
      textValue: 'Archived',
      content: (
        <Badge tone="default" variant="subtle">
          Archived
        </Badge>
      ),
    },
  ]}
/>
```

### Custom Overflow Tooltip

```tsx
<BadgeGroup
  maxVisible={2}
  renderOverflowContent={(overflow) => (
    <div className="flex flex-col gap-1">{overflow.map((item) => item.textValue).join(', ')}</div>
  )}
  items={[
    {
      id: 'React',
      textValue: 'React',
      content: <Badge>React</Badge>,
    },
    {
      id: 'TypeScript',
      textValue: 'TypeScript',
      content: <Badge>TypeScript</Badge>,
    },
    {
      id: 'Tailwind CSS',
      textValue: 'Tailwind CSS',
      content: <Badge>Tailwind CSS</Badge>,
    },
    {
      id: 'Next.js',
      textValue: 'Next.js',
      content: <Badge>Next.js</Badge>,
    },
  ]}
/>
```

### Wrapping Enabled

For cases where you want all badges visible:

```tsx
<BadgeGroup
  wrap
  items={[
    {
      id: 'React',
      textValue: 'React',
      content: <Badge>React</Badge>,
    },
    {
      id: 'TypeScript',
      textValue: 'TypeScript',
      content: <Badge>TypeScript</Badge>,
    },
    {
      id: 'Tailwind CSS',
      textValue: 'Tailwind CSS',
      content: <Badge>Tailwind CSS</Badge>,
    },
  ]}
/>
```

### In ListView

BadgeGroup works well in the ListViewIntermediary slot:

```tsx
<ListView>
  <ListViewStart title="Model Name" />
  <ListViewIntermediary>
    <BadgeGroup
      maxVisible={2}
      items={[
        {
          id: 'React',
          textValue: 'React',
          content: <Badge size="sm">React</Badge>,
        },
        {
          id: 'TypeScript',
          textValue: 'TypeScript',
          content: <Badge size="sm">TypeScript</Badge>,
        },
        {
          id: 'Tailwind',
          textValue: 'Tailwind',
          content: <Badge size="sm">Tailwind</Badge>,
        },
      ]}
    />
  </ListViewIntermediary>
  <ListViewEnd>
    <Button>Configure</Button>
  </ListViewEnd>
</ListView>
```

### In ListCard

BadgeGroup also fits naturally in ListCardBody:

```tsx
<ListCard>
  <ListCardHeader>Model Name</ListCardHeader>
  <ListCardBody>
    <ListCardDescription>Description text...</ListCardDescription>
    <BadgeGroup
      maxVisible={3}
      items={[
        {
          id: 'React',
          textValue: 'React',
          content: <Badge size="sm">React</Badge>,
        },
        {
          id: 'TypeScript',
          textValue: 'TypeScript',
          content: <Badge size="sm">TypeScript</Badge>,
        },
        {
          id: 'Tailwind',
          textValue: 'Tailwind',
          content: <Badge size="sm">Tailwind</Badge>,
        },
      ]}
    />
  </ListCardBody>
</ListCard>
```

## Demo

<ComponentPreview name="badge-group-demo" description="Various BadgeGroup configurations." />
