Kickresume UI

Command Palette

Search for a command to run...

Spinner

An indicator that can be used to show a loading state.

"use client"

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

export default function SpinnerDemo() {
  return <Spinner />
}

Installation

pnpm dlx shadcn@latest add @kickresume/spinner

Usage

import { Spinner } from "@/components/ui/spinner"
<Spinner />

Customization

You can replace the default spinner icon with any other icon by editing the Spinner component.

Loading…
"use client"

import { LoaderIcon } from "lucide-react"

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

function Spinner({ className, ...props }: React.ComponentProps<"svg">) {
  return (
    <LoaderIcon
      data-slot="spinner"
      role="status"
      aria-label="Loading"
      className={cn(
        "size-6 text-primary [animation:spin_1s_steps(8)_infinite]",
        className
      )}
      {...props}
    />
  )
}

export default function SpinnerCustom() {
  return (
    <div className="flex items-center gap-4">
      <Spinner />
    </div>
  )
}
import { LoaderIcon } from "lucide-react"

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

function Spinner({ className, ...props }: React.ComponentProps<"svg">) {
  return (
    <LoaderIcon
      data-slot="spinner"
      role="status"
      aria-label="Loading"
      className={cn("size-6 text-primary [animation:spin_1s_steps(8)_infinite]", className)}
      {...props}
    />
  )
}

export { Spinner }

Examples

Size

The spinner renders at the design system's 20px default. Use the size-* utility class to change the size of the spinner.

Loading…
"use client"

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

export default function SpinnerSize() {
  return (
    <div className="flex items-center gap-6">
      <Spinner className="size-3" />
      <Spinner className="size-4" />
      <Spinner className="size-6" />
      <Spinner className="size-8" />
    </div>
  )
}

Button

Add a spinner to a button to indicate a loading state. Place the <Spinner /> before or after the label — the button spaces its children automatically with gap.

Loading…
"use client"

import { Button } from "@/components/ui/button"
import { Spinner } from "@/components/ui/spinner"

export default function SpinnerButton() {
  return (
    <div className="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

Add a spinner to a badge to indicate a loading state. Place the <Spinner /> before or after the label — the badge spaces its children automatically with gap.

Loading…
"use client"

import { Badge } from "@/components/ui/badge"
import { Spinner } from "@/components/ui/spinner"

export default function SpinnerBadge() {
  return (
    <div className="flex items-center gap-4">
      <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

Loading…
"use client"

import { ArrowUpIcon } from "lucide-react"

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

export default function SpinnerInputGroup() {
  return (
    <div className="flex w-full max-w-md flex-col gap-4">
      <InputGroup>
        <InputGroupInput
          placeholder="Send a message..."
          aria-label="Message"
          disabled
        />
        <InputGroupAddon align="inline-end">
          <Spinner />
        </InputGroupAddon>
      </InputGroup>
      <InputGroup>
        <InputGroupTextarea
          placeholder="Send a message..."
          aria-label="Message"
          disabled
        />
        <InputGroupAddon align="block-end">
          <Spinner /> Validating...
          <InputGroupButton className="ml-auto" variant="default">
            <ArrowUpIcon data-icon="inline-start" />
            <span className="sr-only">Send</span>
          </InputGroupButton>
        </InputGroupAddon>
      </InputGroup>
    </div>
  )
}

Empty

Loading…
"use client"

import { Button } from "@/components/ui/button"
import { Spinner } from "@/components/ui/spinner"

export default function SpinnerEmpty() {
  return (
    <div className="flex w-full flex-col items-center justify-center gap-6 rounded-lg border border-dashed p-8 text-center">
      <div className="flex flex-col items-center gap-4">
        <div className="flex size-10 items-center justify-center rounded-lg bg-muted">
          <Spinner className="size-6" />
        </div>
        <div className="flex flex-col gap-1">
          <div className="text-sm font-medium">Processing your request</div>
          <p className="text-sm text-muted-foreground">
            Please wait while we process your request. Do not refresh the page.
          </p>
        </div>
      </div>
      <Button variant="outline" size="sm">
        Cancel
      </Button>
    </div>
  )
}