Spinner
An animated loading indicator for pending submits, fetches, and inline busy states — a drop-in icon for any neobrutalist control.
import {
Item,
ItemContent,The spinner is a single animated icon that signals an in-flight operation. It wraps the LoaderIcon from lucide-react with animate-spin and ships role="status" plus an aria-label out of the box — no primitive library involved, so both backends share the same implementation, and it inherits currentColor to match whatever neobrutalist control it sits in.
Reach for it when:
- Pending submits — swap it into a button while the mutation runs; the label stays put.
- Inline fetch states — badges, input groups, and table cells waiting on data.
- Section-level loads — pair it with
Emptywhile a panel streams in.
Installation#
pnpm dlx shadcn@latest add https://neobrutalism.com/r/base/spinner.jsonnpx shadcn@latest add https://neobrutalism.com/r/base/spinner.jsonyarn dlx shadcn@latest add https://neobrutalism.com/r/base/spinner.jsonbunx --bun shadcn@latest add https://neobrutalism.com/r/base/spinner.json
Copy and paste the following code into your project.
import { Loader2Icon } from "lucide-react"
import { cn } from "@/lib/utils"
function Spinner({ className, ...props }: React.ComponentProps<"svg">) {
return (
<Loader2Icon
data-slot="spinner"
role="status"
aria-label="Loading"
className={cn("size-4 animate-spin", className)}
{...props}
/>
)
}
export { Spinner }
Update the import paths to match your project setup.
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.
import { LoaderIcon } from "lucide-react"
import { cn } from "@/lib/utils"import { LoaderIcon } from "lucide-react"
import { cn } from "@/lib/utils"
function Spinner({ className, ...props }: React.ComponentProps<"svg">) {
return (
<LoaderIcon
role="status"
aria-label="Loading"
className={cn("size-4 animate-spin", className)}
{...props}
/>
)
}
export { Spinner }Examples#
Size#
Use the size-* utility class to change the size of the spinner.
import { Spinner } from "@/components/ui/spinner"
export function SpinnerSize() {Button#
Add a spinner to a button to indicate a loading state. Place the <Spinner /> before the label with data-icon="inline-start" for a start position, or after the label with data-icon="inline-end" for an end position.
import { Button } from "@/components/ui/button"
import { Spinner } from "@/components/ui/spinner"
Badge#
Add a spinner to a badge to indicate a loading state. Place the <Spinner /> before the label with data-icon="inline-start" for a start position, or after the label with data-icon="inline-end" for an end position.
import { Badge } from "@/components/ui/badge"
import { Spinner } from "@/components/ui/spinner"
Input Group#
import { ArrowUpIcon } from "lucide-react"
import {Empty#
import { Button } from "@/components/ui/button"
import {
Empty,RTL#
To enable RTL support in Neobrutalism.com, see the RTL configuration guide.
"use client"
import * as React from "react"Accessibility#
The spinner renders role="status" with aria-label="Loading", so screen readers announce the loading state without extra wiring. When nearby text already describes the state ("Saving…"), pass aria-hidden="true" to the spinner instead — one announcement is enough. For a whole region that swaps into a loading state, set aria-busy="true" on the container.