Import
import { Spinner } from "@timelycare/helix-ui"
Props
interface SpinnerProps extends React.ComponentProps<"svg"> {
className?: string
}
Styling
| Property | Value | Tailwind |
|---|---|---|
| Size | 16px | size-4 |
| Animation | Rotate | animate-spin |
| Color | Inherits from parent | currentColor |
Typography Context
- Font: Adelle Sans (inherits parent context)
- When used with text (e.g., inside a Button), spacing is handled by the parent's
gaputility
Structure
The Spinner renders an SVG icon (default: LoaderIcon from Lucide) with animate-spin. It uses role="status" and aria-label="Loading" for accessibility.
<LoaderIcon
role="status"
aria-label="Loading"
className="size-4 animate-spin"
/>
Common Patterns
Standalone
<Spinner />
Inside a Button
<Button disabled>
<Spinner data-icon="inline-start" />
Loading...
</Button>
Multiple Variants
<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>
Inline Loading
<div className="flex items-center gap-2 text-muted-foreground">
<Spinner />
<span className="text-sm">Loading...</span>
</div>
Full Page Loading
<div className="flex items-center justify-center min-h-[400px]">
<div className="flex flex-col items-center gap-4">
<Spinner className="size-8 text-primary" />
<p className="text-sm text-muted-foreground">Loading your data...</p>
</div>
</div>
Custom Spinner
You can replace the default icon with any SVG that supports animate-spin:
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}
/>
)
}
Accessibility
- Uses
role="status"to announce loading to assistive technology - Includes
aria-label="Loading"for screen readers - When inside a button, pair with
disabledto prevent interaction during loading - Add
aria-busy="true"on the parent container if content is being replaced
Gotchas
| Problem | Solution |
|---|---|
| Spinner not visible | Ensure parent has a contrasting text color; Spinner inherits currentColor |
| Button still clickable while loading | Always pair Spinner with disabled on the Button |
| Spinner too large/small | Override with className="size-3" or className="size-5" |
| Animation janky | Ensure no conflicting transforms on the parent element |
See Also
- Related Components: Button (loading state), Skeleton (placeholder loading), Progress (determinate loading)
Last updated: February 19, 2026