Source: https://monarchui.dev/components/loading-state/

Components · Status and feedback

# LoadingState

A region is loading: a layout-shaped skeleton or a labelled spinner, announced once.

@rookcom/monarch-react

Component

## List and rows

## Usage examples

### Cards and detail

### Spinner

## Purpose

The placeholder shown while a list, table, card grid or detail view loads. It marks the region busy and announces what is loading once.

### Use when

- A list with avatars is loading (`list`, the default).
- A table or plain list is loading (`rows`).
- A card grid is loading (`cards`).
- A detail view is loading (`detail`).
- The shape of the result is unknown, or a whole card or page is loading (`spinner`).

### Avoid when

- A single control is busy: use Button `loading` or Spinner.
- Loading failed: use ErrorState or InlineErrorRetry.
- There is no data: use EmptyState.

## Design principles

- Match the shape of what is coming, so the page does not jump when it arrives.
- Announce the label once and hide the skeleton shapes from assistive technology.
- A spinner always comes with visible text naming what is loading; never a bare spinner on a page.

## API reference

```tsx
import { LoadingState } from '@rookcom/monarch-react';
```

| Export | Kind | Rendering | Root class |
| --- | --- | --- | --- |
| `LoadingState` | component | server-safe | `.mn-loading-state` |

All native `<div>` attributes pass through, and the ref is forwarded to the root `<div>`.

Before 0.4 LoadingState lived in the error-state folder; the export, props and class names (`mn-loading-state`, `__row`, `__lines`) are unchanged.

Generated from the TypeScript declarations of `packages/react/src/components/feedback/loading-state`.

## Properties

### `LoadingState`

Skeleton placeholder with an aria-busy region and a live announcement.

| Prop | Type | Description |
| --- | --- | --- |
| `label` required | `string` | Announced to screen readers, e.g. "Loading tickets". |
| `children` | `ReactNode` |  |
| `className` | `string` |  |
| `id` | `string` |  |
| `rows` | `number` | Skeleton rows to render (`list`, `rows` and `cards`). Default 4. |
| `variant` | `LoadingStateVariant` | `list` (default) stacks avatar + text rows; `rows` stacks plain text rows (table and list-row preset); `cards` renders a grid; `detail` a title + paragraphs; `spinner` a centred spinner with the label shown. |

## Variants

| Prop | Values | Description |
| --- | --- | --- |
| `variant` | `'list'``'rows'``'cards'``'detail'``'spinner'` | `list` (default) stacks avatar + two text lines; `rows` stacks bordered text rows (table and list-row preset); `cards` a grid of blocks; `detail` a title and paragraphs; `spinner` a centred spinner with the label shown. Exposed as `data-variant`. |
| `rows` | `'number'` | Rows for `list`, `rows` and `cards`. Default 4; negative values render none. |

## States

| State | Exposed as | Behaviour |
| --- | --- | --- |
| Loading | `aria-busy="true"` | The root is always marked busy; unmount it when the content arrives. |

## Interaction behaviour

Not interactive.

## Keyboard support

Not interactive and not focusable.

## Accessibility

Role

status inside an aria-busy region

Accessible name

The

label

prop, e.g. "Loading invoices".

- The label renders in a role="status" element: visually hidden for skeleton variants, visible next to the spinner for `spinner`.
- Skeleton shapes and the spinner are `aria-hidden`.
- Do not nest LoadingState inside another aria-busy region; announce completion from the loaded content if it matters.
- Verified with axe in jsdom and in Chromium, Firefox and WebKit on /components/loading-state.

## Responsive behaviour

- `cards` uses an auto-fill grid with a 14rem minimum column that shrinks to one full-width column below 14rem.
- Skeleton widths are percentages, so rows fit any container down to 320px.

## Theme support

- Skeleton shapes use the Skeleton component (muted surface tokens); `rows` dividers use `--mn-color-border-subtle`.
- The spinner label uses `--mn-color-text-muted`.
- Under `prefers-reduced-motion: reduce` the skeleton shimmer stops and the spinner slows (from Skeleton and Spinner).

## Do and don't

### Do

- Give it a label naming what is loading.
- Pick the variant that matches the content that will replace it.

### Don't

- Do not nest LoadingState inside another aria-busy region.
- Do not leave it mounted after the content arrives.

## Related components

- [Skeleton](https://monarchui.dev/components/skeleton/): The placeholder shapes LoadingState is built from.
- [Spinner](https://monarchui.dev/components/spinner/): A single busy control.
- [ErrorState](https://monarchui.dev/components/error-state/): Loading failed.
- [EmptyState](https://monarchui.dev/components/empty-state/): Loaded, but nothing to show.

## Dependencies

| Kind | Depends on |
| --- | --- |
| npm packages | `react` |
| Monarch components | [Skeleton](https://monarchui.dev/components/skeleton/), [Spinner](https://monarchui.dev/components/spinner/) |

## Testing coverage

- Announces its label as a status inside an aria-busy root.
- Default props keep the 0.3 markup and class names; the ref is forwarded.
- `rows` renders plain text rows without avatars; `cards` and `detail` render their shapes.
- `spinner` shows the label as visible status text and hides the spinner from assistive technology.
- Negative `rows` render no rows.

| Check | Covered |
| --- | --- |
| Axe in jsdom | Yes |
| Server rendering | Yes |
| Browser suites | Visual baselines: /__gallery; Cross-browser axe: /components/loading-state |

Tests: `packages/react/src/components/feedback/loading-state`

## All example code

### List and rows

```tsx
import { LoadingState } from '@rookcom/monarch-react';

<LoadingState label="Loading contacts" rows={3} />
<LoadingState label="Loading invoices" variant="rows" rows={3} />
```

### Cards and detail

```tsx
import { LoadingState } from '@rookcom/monarch-react';

<LoadingState label="Loading projects" variant="cards" rows={3} />
<LoadingState label="Loading project" variant="detail" />
```

### Spinner

```tsx
import { LoadingState } from '@rookcom/monarch-react';

<LoadingState label="Loading report…" variant="spinner" />
```
