<!-- Generated by scripts/gen-docs.mts from the JSDoc in registry/. Do not edit; run `pnpm run docs`. -->

# ItemGroup

A list of `Item` rows, with an optional label that names it, lines between the rows, and what to show when it is empty. It wraps each Item in an `<li>`, so screen readers count the rows.

```tsx
import { ItemGroup } from "@/components/ouro/item/item-group";
```

## Example

```tsx
<ItemGroup label="Bearer credentials" empty="No active bearer credentials.">
  {credentials.map((credential) => (
    <Item key={credential.id} title={credential.name} description={credential.scopes} actions={<Button>Revoke</Button>} />
  ))}
</ItemGroup>
```

## Use instead when

- Use `Stack` instead to space out content that is not a list of rows, and a grid of `Card`s for larger tiles.

## Props

Type: `ItemGroupProps`.

| Prop | Type | Default | Description |
| --- | --- | --- | --- |
| `children` | `ReactNode` |  | The rows: `Item`s. Each one becomes a list item. |
| `className` | `string` |  | Layout only: margins, width, grid placement (`mt-4`). Appearance classes warn in development; use `variant`. |
| `empty` | `ReactNode` |  | What to show in place of the list when there are no rows: a sentence, or an `Empty`. |
| `label` | `string` |  | A short label above the list that also names it for screen readers: "Bearer credentials". Without one, name the list with `aria-label`. |
| `ordered` | `boolean` |  | Renders an ordered list (`<ol>`), for rows whose order means something, such as steps. |
| `ref` | `Ref<HTMLDivElement>` |  | Reaches the rendered element. |
| `variant` | `"outline" \| "plain" \| "divided"` | `"divided"` | `divided` draws a line between rows; `outline` frames them in a bordered surface and pads them, as in a settings screen; `plain` has neither. |

## Native attributes

Also accepts the attributes in React's `HTMLAttributes` (`id`, `aria-*`, event handlers), passed to the rendered element, except those named on this page.

## Don't use

These are not props. Passing one fails the type check with the fix in the error, and in development a console message names the fix.

- `divided`: Not a prop. Lines between rows are the default, `variant="divided"`.
- `items`: Not a prop. ItemGroup takes Items as children: `{rows.map((row) => <Item key={row.id} title={row.name} />)}`.
- `title`: Not a prop. Ouro UI uses `label`: `<ItemGroup label="Bearer credentials">`.

## `className` is for layout only

Pass margins, width and grid placement (`mt-4`, `w-full`, `col-span-2`). Your classes always win over Ouro UI's: ItemGroup merges `className` last with `cn` (tailwind-merge), so a class that conflicts with its own replaces it.[^layout-classes]

Appearance comes from props, not classes. In development, a class that sets appearance logs one warning per component instance, naming the prop to use instead. A class sets appearance when, after its variants (`hover:`, `sm:`) and any `!` are removed, it starts with `bg-`, `text-`, `border-`, `rounded-`, `shadow-`, `font-`, `ring-`, `from-`, `via-`, `to-`, `fill-` or `stroke-`, or is `border`, `rounded`, `shadow` or `ring`, or has a `dark:` variant. Text wrapping, overflow and table layout classes (`text-wrap`, `text-ellipsis`, `border-collapse`) are layout and do not warn.

Alignment classes (`text-center`) warn too: set alignment on a parent element.

[^layout-classes]: test:registry/ouro/components/ouro/item/item-group.test.tsx::ItemGroup > passes attributes and a ref to the wrapper, and lets layout classes win verify:testExists(registry/ouro/components/ouro/item/item-group.test.tsx::ItemGroup > passes attributes and a ref to the wrapper, and lets layout classes win)
