---
type: doc
title: Spinner
description: An animated loading indicator.
---

> Fulldev UI for Astro. The `@fulldev` registry in `components.json` points at `https://ui.full.dev/r/styles/{style}/{name}.json`; see [installation](/astro/docs/installation.md).

```astro title="src/components/examples/spinner/spinner-demo.astro"
---
import { Item, ItemContent, ItemMedia, ItemTitle } from "@/components/ui/item"
import { Spinner } from "@/components/ui/spinner"
---

<div class="flex w-full max-w-xs flex-col gap-4 [--radius:1rem]">
  <Item variant="muted">
    <ItemMedia>
      <Spinner />
    </ItemMedia>
    <ItemContent>
      <ItemTitle class="line-clamp-1">Processing payment...</ItemTitle>
    </ItemContent>
    <ItemContent class="flex-none justify-end">
      <span class="text-sm tabular-nums">$100.00</span>
    </ItemContent>
  </Item>
</div>
```

## Installation

```bash
npx shadcn@latest add @fulldev/spinner
```

## Usage

```ts
import { Spinner } from "@/components/ui/spinner"
```

```astro
<Spinner />
```

`Spinner` renders the Lucide `loader-circle` icon with `role="status"` and
`aria-label="Loading"`. Next to visible loading text, the text can carry the
main status message.

## Customization

Replace the default icon by editing `spinner.astro`.

```astro title="src/components/examples/spinner/spinner-custom.astro"
---
import LoaderIcon from "@lucide/astro/icons/loader"
---

<div class="flex items-center gap-4">
  <LoaderIcon role="status" aria-label="Loading" class="size-4 animate-spin" />
</div>
```

```astro title="src/components/ui/spinner/spinner.astro"
---
import type { HTMLAttributes } from "astro/types"
import LoaderIcon from "@lucide/astro/icons/loader"

import { cn } from "@/lib/utils"

type Props = HTMLAttributes<"svg">

const { class: className, ...props } = Astro.props
---

<LoaderIcon
  data-slot="spinner"
  role="status"
  aria-label="Loading"
  class={cn("size-4 animate-spin", className)}
  {...props}
/>
```

## Size

Use the `size-*` utility class to change the size of the spinner.

```astro title="src/components/examples/spinner/spinner-size.astro"
---
import { Spinner } from "@/components/ui/spinner"
---

<div class="flex items-center gap-6">
  <Spinner class="size-3" />
  <Spinner class="size-4" />
  <Spinner class="size-6" />
  <Spinner class="size-8" />
</div>
```

## Button

Place the `Spinner` before the label with `data-icon="inline-start"`, or after
it with `data-icon="inline-end"`.

```astro title="src/components/examples/spinner/spinner-button.astro"
---
import { Button } from "@/components/ui/button"
import { Spinner } from "@/components/ui/spinner"
---

<div class="flex flex-col items-center gap-4">
  <Button disabled size="sm">
    <Spinner data-icon="inline-start" />
    Loading...
  </Button>
  <Button variant="outline" disabled size="sm">
    <Spinner data-icon="inline-start" />
    Please wait
  </Button>
  <Button variant="secondary" disabled size="sm">
    <Spinner data-icon="inline-start" />
    Processing
  </Button>
</div>
```

## Badge

Place the `Spinner` before the label with `data-icon="inline-start"`, or after
it with `data-icon="inline-end"`.

```astro title="src/components/examples/spinner/spinner-badge.astro"
---
import { Badge } from "@/components/ui/badge"
import { Spinner } from "@/components/ui/spinner"
---

<div class="flex items-center gap-4 [--radius:1.2rem]">
  <Badge>
    <Spinner data-icon="inline-start" />
    Syncing
  </Badge>
  <Badge variant="secondary">
    <Spinner data-icon="inline-start" />
    Updating
  </Badge>
  <Badge variant="outline">
    <Spinner data-icon="inline-start" />
    Processing
  </Badge>
</div>
```

## Input Group

```astro title="src/components/examples/spinner/spinner-input-group.astro"
---
import ArrowUpIcon from "@lucide/astro/icons/arrow-up"

import {
  InputGroup,
  InputGroupAddon,
  InputGroupButton,
  InputGroupInput,
  InputGroupTextarea,
} from "@/components/ui/input-group"
import { Spinner } from "@/components/ui/spinner"
---

<div class="flex w-full max-w-md flex-col gap-4">
  <InputGroup>
    <InputGroupInput placeholder="Send a message..." disabled />
    <InputGroupAddon align="inline-end">
      <Spinner />
    </InputGroupAddon>
  </InputGroup>
  <InputGroup>
    <InputGroupTextarea placeholder="Send a message..." disabled />
    <InputGroupAddon align="block-end">
      <Spinner /> Validating...
      <InputGroupButton class="ml-auto" variant="default">
        <ArrowUpIcon />
        <span class="sr-only">Send</span>
      </InputGroupButton>
    </InputGroupAddon>
  </InputGroup>
</div>
```

## Empty

```astro title="src/components/examples/spinner/spinner-empty.astro"
---
import { Button } from "@/components/ui/button"
import {
  Empty,
  EmptyContent,
  EmptyDescription,
  EmptyHeader,
  EmptyMedia,
  EmptyTitle,
} from "@/components/ui/empty"
import { Spinner } from "@/components/ui/spinner"
---

<Empty class="w-full">
  <EmptyHeader>
    <EmptyMedia variant="icon">
      <Spinner />
    </EmptyMedia>
    <EmptyTitle>Processing your request</EmptyTitle>
    <EmptyDescription>
      Please wait while we process your request. Do not refresh the page.
    </EmptyDescription>
  </EmptyHeader>
  <EmptyContent>
    <Button variant="outline" size="sm">
      Cancel
    </Button>
  </EmptyContent>
</Empty>
```

## RTL

Set `dir="rtl"` on a parent element.

```astro title="src/components/examples/spinner/spinner-rtl.astro"
---
import { Item, ItemContent, ItemMedia, ItemTitle } from "@/components/ui/item"
import { Spinner } from "@/components/ui/spinner"

const translations = {
  en: {
    dir: "ltr",
    values: {
      title: "Processing payment...",
      amount: "$100.00",
    },
  },
  ar: {
    dir: "rtl",
    values: {
      title: "جاري معالجة الدفع...",
      amount: "١٠٠.٠٠ دولار",
    },
  },
  he: {
    dir: "rtl",
    values: {
      title: "מעבד תשלום...",
      amount: "$100.00",
    },
  },
} as const

const { dir, values: t } = translations.ar
---

<div class="flex w-full max-w-xs flex-col gap-4 [--radius:1rem]" dir={dir}>
  <Item variant="muted" dir={dir}>
    <ItemMedia>
      <Spinner />
    </ItemMedia>
    <ItemContent>
      <ItemTitle class="line-clamp-1">{t.title}</ItemTitle>
    </ItemContent>
    <ItemContent class="flex-none justify-end">
      <span class="text-sm tabular-nums">{t.amount}</span>
    </ItemContent>
  </Item>
</div>
```

## API Reference

Source: [`src/components/ui/spinner`](https://github.com/fulldotdev/ui/tree/main/src/components/ui/spinner)

`Spinner` accepts all SVG attributes plus the Lucide icon props `color`,
`size`, `stroke-width` and `absoluteStrokeWidth`.

## Install what this page shows

```bash
npx shadcn@latest add @fulldev/spinner-examples
```
