# Empty state

> A designed empty, error or offline state: a tinted disc pops in, its line art draws itself stroke by stroke with the highlight landing last, then the title, description and actions rise in.

- Type: Component (feedback)
- Access: Free, open source (MIT)
- Page: https://goodui.dev/native/components/empty-state
- Markdown: https://goodui.dev/native/components/empty-state.md
- Registry item: https://goodui.dev/native/r/empty-state.json
- Dependencies: react-native-reanimated, react-native-svg
- Keywords: react native empty state, no results screen, empty list placeholder, error state, offline screen, zero state, list empty component, expo empty state

## When to use

- A list, inbox, calendar or search with nothing to show: ListEmptyComponent of a FlatList.
- A screen that failed to load or cannot load offline, with a retry.
- A first-run screen that explains what will appear and offers the first action.

## When not to use

- While content is loading: use skeleton.
- A problem with part of a screen that otherwise works: use banner.
- Celebrating a finished task: use success-check.

## Installation

With the shadcn CLI (needs a components.json in the Expo app), then `npx expo install --fix` so native dependencies match your Expo SDK. The foundation comes along the first time.

```bash
npx shadcn@latest add https://goodui.dev/native/r/empty-state.json
npx expo install --fix
```

The component lands in `components/goodui-native/empty-state/` and imports the foundation from `components/goodui-native/foundation/` with relative paths; it also installs button, locale.

Built-in strings follow `<GooduiLocaleProvider>` (one [language pack](https://goodui.dev/native/r/locale-tr.json) translates the whole kit); the `labels` prop overrides them for one instance.

## Usage

```tsx
import { EmptyState } from "@/components/goodui-native/empty-state/empty-state";

export function NoBookings({ onBook }: { onBook: () => void }) {
  return (
    <EmptyState
      preset="no-bookings"
      description="Classes you book show up here with a reminder an hour before."
      action={{ label: "Find a class", onPress: onBook }}
    />
  );
}
```

## Examples

### Search with no results, inside a list

```tsx
<FlatList
  data={results}
  renderItem={renderRow}
  ListEmptyComponent={
    <EmptyState
      preset="no-results"
      size="sm"
      title={`No results for "${query}"`}
      secondaryAction={{ label: "Clear filters", onPress: clearFilters }}
    />
  }
/>
```

### Error with an async retry

```tsx
<EmptyState preset="error" action={{ label: "Try again", onPress: () => refetch() }} secondaryAction={{ label: "Contact support", onPress: openSupport }} />
```

### Your own art

```tsx
<EmptyState illustration={<Image source={require("./inbox.png")} style={{ width: 120, height: 120 }} />} title="Inbox zero" description="Nice work." />
```

## API reference

### EmptyState

`import { EmptyState } from "@/components/goodui-native/empty-state/empty-state";`

| Prop | Type | Default | Description |
| --- | --- | --- | --- |
| `preset` | `"error" \| "offline" \| "no-results" \| "no-messages" \| "no-bookings"` | – | Ready-made art and copy for the common cases. Your `title` and `description` replace the copy. |
| `title` | `string` | – | What is empty, in a few words. Required without a preset. |
| `description` | `string` | – | What to do about it, in one or two short sentences. |
| `illustration` | `ReactNode \| false` | – | Your own art (an image, an svg) instead of the preset line art. `false` shows no art. |
| `tone` | `"success" \| "warning" \| "danger" \| "accent" \| "neutral"` | – | Color of the art's highlight stroke and the disc behind it. Presets pick one (error is danger, offline warning). |
| `size` | `"sm" \| "md"` | `"md"` | `md` for a whole screen or a large panel, `sm` inside a list, a card or a sheet. |
| `action` | `EmptyStateAction` | – | The one way forward, a primary button. |
| `secondaryAction` | `EmptyStateAction` | – | A quieter second option, a ghost button. |
| `animated` | `boolean` | `true` | Draw the art in and let the text rise. `false` shows everything at once. Default `true`. |
| `labels` | `Partial<EmptyStateLabels>` | – | Overrides for the built-in preset copy; a GooduiLocaleProvider pack sets it app-wide. |
| `style` | `StyleProp<ViewStyle>` | – | Style for the outer container. |
| `testID` | `string` | – | Identifier for end-to-end tests. |

### EmptyStateIllustration

`import { EmptyStateIllustration } from "@/components/goodui-native/empty-state/empty-state";`

| Prop | Type | Default | Description |
| --- | --- | --- | --- |
| `name` (required) | `"error" \| "offline" \| "no-results" \| "no-messages" \| "no-bookings"` | – | Which line art. |
| `size` | `number` | `120` | Width and height in points. Default 120. |
| `tone` | `"success" \| "warning" \| "danger" \| "accent" \| "neutral"` | – | – |
| `animated` | `boolean` | `true` | Draw the strokes in, one after another. Default `true`. |
| `playKey` | `string \| number` | – | Change it to draw the art again. |

## Accessibility

- The title has the header role. The art is hidden from screen readers (no-hide-descendants, aria-hidden).
- Actions are goodui Buttons: role button, busy while a returned promise is pending, with announced success and error.
- Text is centered with a 360 pt measure; long titles wrap.

## Motion

- The disc behind the art scales from 0.6 on the bouncy spring; the strokes draw in order (each in its own overlapping window over about a second), neutral strokes first and the highlight strokes last.
- Title, description and actions rise 10 pt and fade in on the smooth spring, staggered 70 ms, after the art has started.
- The art draws again and the text rises again when the preset, title or description changes, for example a new search query.
- Reduced motion: no drawing and no rise; the art and text fade in.
- animated={false} shows everything at once.

## Haptics

- None of its own; action buttons tap on press and play success or error for returned promises.

## Performance

- Each stroke is one animated svg path driven by one shared progress value on the UI thread; a preset has 5 to 10 strokes.

## Notes for AI

- Pick a preset for the common cases and override title and description with your own copy; the preset decides the art and the tone (error is danger, offline warning).
- Use size='sm' inside lists, cards and sheets; md for a whole screen.
- Pass at most one action (primary button) and one secondaryAction (ghost button). onPress may return a promise.
- illustration={false} removes the art; any ReactNode replaces it. EmptyStateIllustration renders the line art alone.

## Related

- [Skeleton](https://goodui.dev/native/components/skeleton.md): Loading placeholders whose shimmer is one light passing diagonally over the whole screen, plus presets and a Swap that eases from the skeleton's height to the content's while it cross-fades.
- [Banner](https://goodui.dev/native/components/banner.md): An inline status banner that slides down from under the edge above and pushes the content down, flings away sideways with the finger, and can collapse on its own behind a draining hairline; includes an offline preset.
- [Button](https://goodui.dev/native/components/button.md): A press-responsive button with haptics that runs async actions on its own: it gathers into a spinner, then opens into a success or error state.
- [Success check](https://goodui.dev/native/components/success-check.md): A success or error moment: the circle springs in, the check draws itself while a ring pulses out and the title rises in; the error variant draws a cross and shakes once. Inline or fullscreen, optionally with confetti.

## Guidance for AI tools

Empty state: A designed empty, error or offline state: a tinted disc pops in, its line art draws itself stroke by stroke with the highlight landing last, then the title, description and actions rise in. Use only the documented props. Keep accessibility, reduced motion and both color schemes intact when adapting it.

Full library index: https://goodui.dev/native/llms.txt
