BadgeGroup
A composable component for displaying a group of badges with overflow handling and "+N more" pattern.
Installation
npx shadcn@latest add @redpanda/badge-grouppnpm dlx shadcn@latest add @redpanda/badge-groupyarn dlx shadcn@latest add @redpanda/badge-groupbunx shadcn@latest add @redpanda/badge-groupnubx shadcn@latest add @redpanda/badge-groupaube dlx shadcn@latest add @redpanda/badge-groupUsage
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.
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
// 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
<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
<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:
<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:
<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:
<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>