---
title: Dot Group
description: A positioning container that pins indicator dots to the corners of content elements, with optional overflow handling and standalone stacking mode.
icon: layers
author:
  name: eblairmckee
  url: https://github.com/eblairmckee
mode: wide
---

### Installation

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

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

## Usage

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

{
  /* Position dots on content */
}
<DotGroup
  content={
    <Badge tone="default" variant="subtle">
      kafka
    </Badge>
  }
  size="sm"
  items={[
    {
      id: 'errors',
      kind: 'status',
      variant: 'destructive',
    },
    {
      id: 'warnings',
      kind: 'status',
      variant: 'warning',
    },
  ]}
/>;

{
  /* Standalone stacking (no content) */
}
<DotGroup
  size="sm"
  items={[
    {
      id: 'errors',
      kind: 'status',
      variant: 'destructive',
    },
    {
      id: 'warnings',
      kind: 'status',
      variant: 'warning',
    },
  ]}
/>;
```

Descriptors use caller-owned, nonempty, unique IDs. Editing a count or changing order retains that indicator’s DOM node; changing its ID intentionally replaces it. `label` supplies an optional complete accessible name, including the count when relevant. The group owns size, overlap, and stack order; standalone StatusDot and CountDot remain unchanged.

## When to use

**Use DotGroup when:**

- Positioning indicator dots on a badge, avatar, or other content element
- Displaying multiple status or count indicators that need compact stacking
- You need to truncate many indicators to a visible subset (`maxVisible` prop)

**Don't use DotGroup when:**

- You only have a single indicator without content to attach it to (use [StatusDot](/docs/status-dot) or [CountDot](/docs/count-dot) directly)
- You need a label alongside the indicator (use [StatusBadge](/docs/status-badge))
- On a [StatusBadge](/docs/status-badge) — it already composes StatusDot internally

## Modes

### Content positioning mode

When `content` is provided, DotGroup wraps the content in a relative container and positions the dots at the specified corner. This is the primary use case.

```tsx
<DotGroup
  content={
    <Badge tone="default" variant="subtle">
      Service
    </Badge>
  }
  size="sm"
  items={[
    {
      id: 'errors',
      kind: 'status',
      variant: 'destructive',
    },
  ]}
/>
```

### Standalone mode

Without `content`, DotGroup renders dots in a horizontal stack with overlapping negative margins. This is useful for inline indicator groups.

```tsx
<DotGroup
  size="sm"
  items={[
    {
      id: 'errors',
      kind: 'status',
      variant: 'destructive',
    },
    {
      id: 'warnings',
      kind: 'status',
      variant: 'warning',
    },
  ]}
/>
```

## Pattern: Same-type grouping

Prefer same-type indicators for consistent visual weight. Items are explicit `status` or `count` descriptors with stable IDs. Status-only groups support every dot size; any count indicator requires `sm`, `md`, or `lg`. Arbitrary JSX children and implicit prop injection are no longer supported.

```tsx
// StatusDot group
<DotGroup content={<Badge tone="default" variant="subtle">kafka</Badge>} size="sm" items={[
    {
        id: "errors",
        kind: "status",
        variant: "destructive"
    },
    {
        id: "warnings",
        kind: "status",
        variant: "warning"
    }
]}/>

// CountDot group
<DotGroup content={<Badge tone="default" variant="subtle">broker-0</Badge>} size="sm" items={[
    {
        id: "warnings",
        kind: "count",
        count: 5,
        variant: "warning"
    },
    {
        id: "errors",
        kind: "count",
        count: 2,
        variant: "destructive"
    }
]}/>
```

## Pattern: Overflow

When `maxVisible` is set and items exceed it, DotGroup renders the first N dots followed by a `+` icon signaling that more indicators exist. The icon sizes and contrasts automatically with the dots.

```tsx
<DotGroup
  content={
    <Badge tone="default" variant="subtle">
      events
    </Badge>
  }
  maxVisible={3}
  size="sm"
  items={[
    {
      id: 'errors',
      kind: 'status',
      variant: 'destructive',
    },
    {
      id: 'warnings',
      kind: 'status',
      variant: 'warning',
    },
    {
      id: 'information',
      kind: 'status',
      variant: 'informative',
    },
    {
      id: 'healthy',
      kind: 'status',
      variant: 'success',
    },
    {
      id: 'disabled',
      kind: 'status',
      variant: 'disabled',
    },
  ]}
/>;
{
  /* Shows the first 3 dots + overflow indicator */
}
```

## Anatomy

```
Content positioning mode
┌──────────────────────────────────┐
│                        [●][●]   │  ← absolute positioned at corner
│  Badge Label                    │
│                                 │
└──────────────────────────────────┘

Standalone mode
[●][●][●]  ← overlapping inline indicators with ring separation
```

## API Reference

### `DotGroup`

A status descriptor has `id`, `kind: 'status'`, optional `variant`, `pulsing`, and `label`. A count descriptor has `id`, `kind: 'count'`, required `count`, and optional `variant`, `max`, and `label`. `label` is the complete accessible indicator name; unlabelled dots remain decorative. Group IDs must be nonempty and unique. `maxVisible` must be a nonnegative integer.

| Prop         | Type                                                                                                     | Default       | Description                                                           |
| ------------ | -------------------------------------------------------------------------------------------------------- | ------------- | --------------------------------------------------------------------- |
| `items`      | `readonly DotGroupItem[]`                                                                                | —             | Explicit status/count descriptors with stable IDs                     |
| `content`    | `ReactNode?`                                                                                             | —             | Element to position dots relative to                                  |
| `position`   | `"top-right" \| "top-left" \| "bottom-right" \| "bottom-left" \| "top" \| "bottom" \| "left" \| "right"` | `"top-right"` | Position for dots when using content mode (corners or edge-centers)   |
| `size`       | `"xxs" \| "xs" \| "sm" \| "md" \| "lg"`                                                                  | `"md"`        | Controls owned dot size and overlap; count items require sm or larger |
| `maxVisible` | `number?`                                                                                                | —             | Maximum dots to show; overflow renders a `+` icon                     |
| `testId`     | `string?`                                                                                                | —             | Test ID for visual regression testing                                 |
| `className`  | `string?`                                                                                                | —             | Additional CSS classes                                                |

## Credits

- Follows the overlapping stack pattern from [AvatarGroup](/docs/avatar-group) but simplified for indicator use cases.
- Designed to compose with [StatusDot](/docs/status-dot) and [CountDot](/docs/count-dot).
