All components
Data Display

TagGroup

stable
Since 1.1
A row of tags: static labels, or single/multiple selection, with per-tag and group removal, dots, counts, and avatar slots.
Data Display
primitive
Interactive
Needs State Matrix
import { TagGroup } from '@ivim/native/components';

When to use

Use when
Showing a set of applied filters, labels, or categories.
Offering a compact multi-select over a small number of options.
Displaying removable values entered elsewhere, such as recipients.
Don't use when
The tags are a mutually exclusive view switcher — use ButtonGroup, which reads as one joined control.
A single value conveys status and is not interactive — use Badge.
There are many options to choose among — use MultiSelect so the list can scroll and filter.

Playground

Adjust the controls to preview live prop combinations. The code snippet updates to match the current selection.
Selection mode
selectionMode
size
size
Disabled
isDisabled
<TagGroup selectionMode="none" />

Example

Selection modes
Multiple selection
Single selection
Static (no role, no press target)
Removal
Removable, with clear all
Removable and selectable together
Adornments
Status dots
Counts
Avatars
Sizes (sm, md, lg)
Disabled
One tag disabled
Whole group disabled

Anatomy

The parts that compose this component.
1
Tag
The pill: border, fill, and label. Pressable only when selection is enabled.
2
Dot
Optional status indicator before the label.
3
Leading slot
Optional avatar, flag, or custom element.
4
Count
Optional trailing number badge.
5
Remove control
A separate named button beside the tag, present only when onRemove is supplied.
6
Clear all
Trailing group-level removal, present only when onRemoveAll is supplied.

Guidelines

Do
Use selectionMode="none" for tags that are purely informational, so no useless action is announced.
Do
Treat onRemove as a report: filter your own list in response, so the source of truth stays with the caller.
Do
Pair a dot with text rather than relying on dot color alone to convey status.
Don't
Make a tag both the only way to select and the only way to remove — they are separate actions and need separate controls.
Don't
Use tags for primary navigation; they read as values, not destinations.

Props

Public props for this component. Required props are marked; defaults reflect the component implementation.
items
required
TagGroupItem[]
{ value, label, isDisabled?, dot?, dotColor?, leading?, count? }.
selectionMode
'none' | 'single' | 'multiple'
default: 'none'
none renders static, non-interactive tags with no role or press target.
selectedKeys / defaultSelectedKeys
string[]
Controlled and uncontrolled selection. Always an array, including in single mode.
onSelectionChange
(keys: string[]) => void
Fires with the next selection.
onRemove
(value: string) => void
Enables a per-tag remove control. Reports the removal; the caller owns the list.
onRemoveAll
() => void
Renders a trailing "Clear all" control.
isDisabled
boolean
default: false
Disables every tag, overriding per-item state.
size
'sm' | 'md' | 'lg'
default: 'md'
Tag height, padding, and label scale.
aria-label
string
Accessible name when there is no visible label, or when the label is not plain text.

Deviations from Untitled UI

Where this component deliberately differs from Untitled UI's API, and why. These are kept choices, not gaps to close.
Untitled UI
Compound children (`Select.Item`, `Dropdown.Root/Popover/Menu`, `Tabs.List/Panel`)
Ours
data-driven `items` / `options` / `columns` arrays
RN has no DOM portals and compound context patterns complicate static typing and testing. Typed arrays are serializable, exhaustively checkable, and match RN list idioms. See grammar rule G8.
Untitled UI
item `id`
Ours
item `value`
`value` is the library-wide key prop on all option/item types (G8); one name everywhere.

Accessibility

Roles and screen-reader behavior this component provides, plus keyboard interactions.
Tags announce as checkbox in multiple mode and radio in single mode, carrying selected state.
With selectionMode="none" no role or press target is rendered at all, so assistive technology does not offer an action that does nothing.
Removal is a separate control with its own name ("Remove Design"), so removing a tag never selects it and vice versa.
The group container is intentionally not an accessibility element, so each tag stays individually reachable on iOS.
Disabled tags can neither be selected nor removed, and a group-level isDisabled overrides every tag.
Tab
Moves focus to each tag, then to its remove control.
Space / Enter
Toggles selection, or activates the focused remove control.

Platform & RTL

Behavior that differs across the platforms this component runs on.
Web
Every tag and remove control is focusable with a visible inset focus ring.
iOS
VoiceOver reaches the tag and its remove control as separate elements.
Android
TalkBack reaches the tag and its remove control as separate elements.
RTL
Tag order, leading slots, and the remove control mirror with the writing direction.

State coverage

Interaction and visual states this component supports.
default
pressed
disabled
active

Token references

Semantic design tokens this component page exercises. Tap one to edit it live.