import { Button } from "@registry/components/ui/button";
export function ButtonExample() {
return (
<div className="flex flex-wrap items-center gap-4">
<Button>Button</Button>
</div>
);
} Examples
Variants
Secondary
import { Button } from "@registry/components/ui/button";
export function SecondaryButtonExample() {
return (
<div className="flex flex-wrap items-center gap-4">
<Button variant="secondary">Secondary</Button>
</div>
);
} Outline
import { Button } from "@registry/components/ui/button";
export function OutlineButtonExample() {
return (
<div className="flex flex-wrap items-center gap-4">
<Button variant="outline">Outline</Button>
</div>
);
} Ghost
import { Button } from "@registry/components/ui/button";
export function GhostButtonExample() {
return (
<div className="flex flex-wrap items-center gap-4">
<Button variant="ghost">Ghost</Button>
</div>
);
} Destructive
import { Button } from "@registry/components/ui/button";
export function DestructiveButtonExample() {
return (
<div className="flex flex-wrap items-center gap-4">
<Button variant="destructive">Destructive</Button>
</div>
);
} Revert
import { Button } from "@registry/components/ui/button";
export function RevertButtonExample() {
return (
<div className="flex flex-wrap items-center gap-4 bg-gray-950 dark:bg-gray-50 p-4 rounded w-full h-32 justify-center">
<Button variant="revert">Revert</Button>
</div>
);
} Sizes
Available sizes: default, sm, lg, icon, icon-sm.
import { Button } from "@registry/components/ui/button";
import { PlusIcon } from "@phosphor-icons/react";
export function ButtonSizesExample() {
return (
<div className="flex flex-wrap items-center gap-4">
<Button size="sm">Small</Button>
<Button size="default">Default</Button>
<Button size="lg">Large</Button>
<Button size="icon">
<PlusIcon />
</Button>
<Button size="icon-sm">
<PlusIcon />
</Button>
</div>
);
} Icon
Use the icon or icon-sm size with an SVG icon inside the button.
import { Button } from "@registry/components/ui/button";
import { PlusIcon } from "@phosphor-icons/react";
export function IconButtonExample() {
return (
<div className="flex flex-wrap items-center gap-4">
<Button variant="outline" size="icon">
<PlusIcon />
</Button>
</div>
);
} Loading state
Pass the loading prop to show a spinner and blur the button content:
import { Button } from "@registry/components/ui/button";
import { useState } from "react";
export function LoadingButtonExample() {
const [loading, setLoading] = useState(false);
return (
<Button
loading={loading}
onClick={() => {
setLoading(true);
setTimeout(() => setLoading(false), 1000);
}}
>
Click me
</Button>
);
} Installation
Copy the source code below into your project:
import { Button as BaseButton } from "@base-ui/react/button";
import { cn } from "@registry/lib/utils";
import { SpinnerIcon } from "@phosphor-icons/react";
import { cva } from "class-variance-authority";
import type { VariantProps } from "class-variance-authority";
import { AnimatePresence, motion } from "motion/react";
const buttonVariants = cva(
"font-regular data-disabled:pointer-not-allowed relative inline-flex shrink-0 items-center justify-center gap-2 overflow-hidden rounded-lg text-sm whitespace-nowrap transition-all outline-none focus-visible:ring-[3px] focus-visible:ring-ring not-data-disabled:active:scale-[0.98] aria-invalid:border-destructive aria-invalid:ring-destructive/20 data-disabled:cursor-not-allowed data-disabled:opacity-50 dark:aria-invalid:ring-destructive/40 [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4",
{
defaultVariants: {
size: "default",
variant: "default",
},
variants: {
size: {
default: "h-9 px-4 py-2 has-[>svg]:px-3",
icon: "size-9",
"icon-sm": "size-8",
lg: "h-10 px-6 has-[>svg]:px-4",
sm: "h-8 gap-1.5 px-3 has-[>svg]:px-2.5",
},
variant: {
default:
"bg-primary text-primary-foreground not-data-disabled:hover:bg-primary/90 not-data-disabled:active:bg-primary/80 data-popup-open:bg-primary/90",
destructive:
"bg-destructive text-destructive-foreground not-data-disabled:hover:bg-destructive/90 focus-visible:ring-destructive/30 data-popup-open:bg-destructive/90",
ghost:
"bg-transparent text-foreground not-data-disabled:hover:bg-secondary data-popup-open:bg-secondary",
outline:
"border border-input text-foreground not-data-disabled:hover:bg-secondary data-popup-open:bg-secondary",
revert:
"bg-muted text-foreground not-data-disabled:hover:bg-muted/80 data-popup-open:bg-muted/80",
secondary:
"bg-secondary text-secondary-foreground not-data-disabled:hover:bg-secondary/60 data-popup-open:bg-secondary/60",
},
},
},
);
type ButtonProps = BaseButton.Props &
VariantProps<typeof buttonVariants> & {
loading?: boolean;
};
function Button({ variant, size, className, children, loading, disabled, ...props }: ButtonProps) {
return (
<BaseButton
className={cn(
buttonVariants({ className, size, variant }),
loading && "data-disabled:cursor-wait",
)}
disabled={loading || disabled}
data-slot="button"
focusableWhenDisabled
{...props}
>
<motion.span
animate={{
filter: loading ? "blur(4px)" : "blur(0px)",
opacity: loading ? 0 : 1,
scale: loading ? 0.8 : 1,
}}
className="flex items-center gap-2"
transition={{ duration: 0.2 }}
initial={false}
>
{children}
</motion.span>
<AnimatePresence initial={false}>
{loading && (
<motion.span
animate={{ filter: "blur(0px)", opacity: 1, scale: 1 }}
className="absolute inset-0 flex items-center justify-center"
exit={{ filter: "blur(4px)", opacity: 0, scale: 0.8 }}
initial={{ filter: "blur(4px)", opacity: 0, scale: 0.8 }}
transition={{ duration: 0.2 }}
>
<SpinnerIcon className="size-4 animate-spin" />
</motion.span>
)}
</AnimatePresence>
</BaseButton>
);
}
export { Button, buttonVariants }; API Reference
The Button component extends the Base UI Button
props and adds the following:
| Prop | Type | Default | Description |
|---|---|---|---|
| variant | "default" | "secondary" | "outline" | "ghost" | "destructive" | "revert" | "default" | Visual style of the button. |
| size | "default" | "sm" | "lg" | "icon" | "icon-sm" | "default" | Size of the button. |
| loading | boolean | false | When true, shows a spinner and disables the button. |