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

# ShowMore

"Show more" for a long list or a long text. It cuts a list to its first items, clamps text to a number of lines, or grows a list a page at a time from the server, and moves focus to the first newly shown item, so keyboard and screen reader users land on what they asked for. A list that loads more also says how many items now show. The visible items are in the server HTML. ShowMore draws the list itself: put a border around the list on a wrapping element, and borders between rows on each item.

```tsx
import { ShowMore } from "@/components/ouro/collapsible/show-more";
```

## Example

```tsx
<ShowMore items={reviews.map((review) => <Review key={review.id} {...review} />)} initialCount={3} />
<ShowMore lines={3}>{recipe.description}</ShowMore>
<ShowMore items={rows} hasMore={Boolean(nextCursor)} loading={loading} onShowMore={loadNext} href={nextHref} />
```

## Use instead when

- Use `Collapsible` instead to hide a whole section behind its title.
- Use `Pagination` instead when people need to jump to a page or share its URL.

## Props

The props the modes share. Type: `ShowMoreProps`.

| Prop | Type | Default | Description |
| --- | --- | --- | --- |
| `className` | `string` |  | Layout only: margins, width, grid placement (`mt-6`). Text classes go on a parent so the items inherit them. |
| `ref` | `Ref<HTMLDivElement>` |  | Reaches the rendered element. |
| `showMoreLabel` | `string` | `"Show more"` | The button's text while there is more to show. Translate it here. |

## Modes

`ShowMoreProps` is a union: pass the props of exactly one mode. Mixing them is a type error.

### Items: `ShowMoreItemsProps`

A list cut to its first `initialCount` items, with "Show more" and "Show less".

| Prop | Type | Default | Description |
| --- | --- | --- | --- |
| `initialCount` | `number` | `5` | How many items show while collapsed. |
| `items` (required) | `readonly ReactNode[]` |  | Every item, in order. Each renders in its own list item; give elements a `key`. With `onShowMore`, append each new page here. |
| `ordered` | `boolean` | `false` (`<ul>`) | Renders an `<ol>`, for a ranked or numbered list. |
| `showLessLabel` | `string` | `"Show less"` | The button's text while everything shows. Translate it here. |

Not in this mode (typed `never`):

- `children`: Text mode only: a list takes `items`.
- `hasMore`: Only with `onShowMore`.
- `href`: Only with `onShowMore`.
- `lines`: Text mode only: not with `items`.
- `loadedLabel`: Only with `onShowMore`.
- `loading`: Only with `onShowMore`.
- `loadingLabel`: Only with `onShowMore`.
- `noMoreLabel`: Only with `onShowMore`.
- `onShowMore`: Only for a list that loads more: with `items`, without `initialCount` or `lines`.

### Lines: `ShowMoreLinesProps`

Long text clamped to `lines` lines, with "Show more" and "Show less".

| Prop | Type | Default | Description |
| --- | --- | --- | --- |
| `children` (required) | `ReactNode` |  | The text. Screen readers always read all of it. |
| `lines` (required) | `number` |  | How many lines show while collapsed. The button appears only when the text is longer. |
| `showLessLabel` | `string` | `"Show less"` | The button's text while everything shows. Translate it here. |

Not in this mode (typed `never`):

- `hasMore`: Only with `onShowMore`.
- `href`: Only with `onShowMore`.
- `initialCount`: Only with `items` and no `onShowMore`.
- `items`: List modes only: not with `lines`.
- `loadedLabel`: Only with `onShowMore`.
- `loading`: Only with `onShowMore`.
- `loadingLabel`: Only with `onShowMore`.
- `noMoreLabel`: Only with `onShowMore`.
- `onShowMore`: Only for a list that loads more: with `items`, without `initialCount` or `lines`.
- `ordered`: List modes only: not with `lines`.

### Load: `ShowMoreLoadProps`

A list the server grows a page at a time: "Show more" asks for the next page and appends it.

| Prop | Type | Default | Description |
| --- | --- | --- | --- |
| `hasMore` (required) | `boolean` |  | Whether there is another page. Without one, the button goes away. |
| `href` | `string` |  | The next page's URL, so the button is a link that works before JavaScript loads. With JavaScript, pressing it calls `onShowMore` instead. |
| `items` (required) | `readonly ReactNode[]` |  | Every item, in order. Each renders in its own list item; give elements a `key`. With `onShowMore`, append each new page here. |
| `loadedLabel` | `((count: number) => string)` | (count) => `Showing ${count} items` | What screen readers hear after each page, from the number of items now shown. Name the items and translate it here: `(count) => \`Showing ${count} recipes\``. |
| `loading` | `boolean` |  | Shows the button as busy, and keeps it from asking twice. |
| `loadingLabel` | `string` | `"Loading"` | What screen readers hear when loading starts. Translate it here. |
| `noMoreLabel` | `string` | `"No more items"` | What screen readers hear when a page comes back empty and there is no more. Translate it here. |
| `onShowMore` (required) | `() => void` |  | Asks for the next page. The button is busy while `loading`, and focus moves to the first new item when it arrives. |
| `ordered` | `boolean` | `false` (`<ul>`) | Renders an `<ol>`, for a ranked or numbered list. |

Not in this mode (typed `never`):

- `children`: Text mode only: a list takes `items`.
- `initialCount`: Only with `items` and no `onShowMore`.
- `lines`: Text mode only: not with `items`.
- `showLessLabel`: A list that loads more never shrinks, so it has no "Show less".

## 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.

- `limit`: Not a prop. Ouro UI uses `initialCount`: `<ShowMore items={items} initialCount={5}>`.
- `maxLines`: Not a prop. Ouro UI uses `lines`: `<ShowMore lines={3}>`.

## `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: ShowMore 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/collapsible/show-more.test.tsx::ShowMore with items > lets app layout classes win and keys rows by their own keys verify:testExists(registry/ouro/components/ouro/collapsible/show-more.test.tsx::ShowMore with items > lets app layout classes win and keys rows by their own keys)
