# Spinner

> Atom — orn-ui

> ⚠️ Requires <UIProvider>: This component (like every orn-ui component) must render inside a <UIProvider> ancestor, or it throws at runtime. See https://orn-ui-docs.vercel.app/getting-started.md

> Runs on Expo SDK 54, 55, 56 and 57, and on bare React Native >=0.81 with react >=19.1. No native modules — works in Expo Go, no prebuild.

## Installation

Install just this component (copies the source into your project, no npm dependency)
```bash
npx orn-ui add spinner
```

Or install the whole package and import it
```tsx
pnpm add orn-ui
import { Spinner } from 'orn-ui/spinner';
```

Depends on: core (theme + icons)

## Variants

### Default — a status message under the spinner

```tsx
<Spinner text="Loading invoices..." fullscreen={false} />
```

### Custom look — dots and ring, identical on iOS and Android

```tsx
<VariantRow>
  <Spinner variant="dots" text="Fetching..." fullscreen={false} />
  <Spinner variant="ring" text="Syncing..." fullscreen={false} />
</VariantRow>
```

### Sizes — small for inline use, large for a loading screen

```tsx
<VariantRow>
  <Spinner variant="ring" size="small" fullscreen={false} />
  <Spinner variant="ring" size="large" fullscreen={false} />
</VariantRow>
```

### Custom color — matches your brand accent

```tsx
<VariantRow>
  <Spinner variant="dots" color="#00cae1" fullscreen={false} />
  <Spinner variant="ring" color="#ff3b30" fullscreen={false} />
</VariantRow>
```

### Your own indicator — e.g. an avatar while a photo uploads

```tsx
<Spinner
  fullscreen={false}
  text="Uploading..."
  indicator={
    <Avatar size={48} backgroundColor="#004cef20">
      <Icon name="plus" size={24} color="#004cef" />
    </Avatar>
  }
/>
```

### No text — just the wait, next to a button or inline in a row

```tsx
<View style={{ alignItems: 'center' }}>
  <Spinner variant="ring" fullscreen={false} />
</View>
```

## Full demo source

```tsx
const variants: VariantDef[] = [
  {
    label: 'Default — a status message under the spinner',
    content: <Spinner text="Loading invoices..." fullscreen={false} />,
  },
  {
    label: 'Custom look — dots and ring, identical on iOS and Android',
    content: (
      <VariantRow>
        <Spinner variant="dots" text="Fetching..." fullscreen={false} />
        <Spinner variant="ring" text="Syncing..." fullscreen={false} />
      </VariantRow>
    ),
  },
  {
    label: 'Sizes — small for inline use, large for a loading screen',
    content: (
      <VariantRow>
        <Spinner variant="ring" size="small" fullscreen={false} />
        <Spinner variant="ring" size="large" fullscreen={false} />
      </VariantRow>
    ),
  },
  {
    label: 'Custom color — matches your brand accent',
    content: (
      <VariantRow>
        <Spinner variant="dots" color="#00cae1" fullscreen={false} />
        <Spinner variant="ring" color="#ff3b30" fullscreen={false} />
      </VariantRow>
    ),
  },
  {
    label: 'Your own indicator — e.g. an avatar while a photo uploads',
    content: (
      <Spinner
        fullscreen={false}
        text="Uploading..."
        indicator={
          <Avatar size={48} backgroundColor="#004cef20">
            <Icon name="plus" size={24} color="#004cef" />
          </Avatar>
        }
      />
    ),
  },
  {
    label: 'No text — just the wait, next to a button or inline in a row',
    content: (
      <View style={{ alignItems: 'center' }}>
        <Spinner variant="ring" fullscreen={false} />
      </View>
    ),
  },
];
return <VariantList variants={variants} />;
```

## Props

| Name | Type | Default | Description |
|---|---|---|---|
| `size?` | `'small' \| 'large'` | large | — |
| `color?` | `string` | — | — |
| `text?` | `string` | — | — |
| `fullscreen?` | `boolean` | true | When false, doesn't take up flex:1 (useful inline, inside a button/row). |
| `variant?` | `'native' \| 'dots' \| 'ring'` | native | 'native' uses the system ActivityIndicator; 'dots' and 'ring' are custom indicators, identical on iOS and Android. |
| `indicator?` | `ReactNode` | — | Fully custom indicator. Wins over `variant`. |
