Button

Displays a button or a component that looks like a button.

ButtonExample

Examples

Variants

Secondary

SecondaryButtonExample

Outline

OutlineButtonExample

Ghost

GhostButtonExample

Destructive

DestructiveButtonExample

Revert

RevertButtonExample

Sizes

Available sizes: default, sm, lg, icon, icon-sm.

ButtonSizesExample

Icon

Use the icon or icon-sm size with an SVG icon inside the button.

IconButtonExample

Loading state

Pass the loading prop to show a spinner and blur the button content:

LoadingButtonExample

Installation

Copy the source code below into your project:

tsx
import { Button as BaseButton } from "@base-ui/react/button";
import { cn } from "@/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-hover data-popup-open:bg-primary-hover",
        destructive:
          "bg-destructive text-destructive-foreground not-data-disabled:hover:bg-destructive-hover focus-visible:ring-destructive/30 data-popup-open:bg-destructive-hover",
        ghost:
          "bg-transparent text-foreground not-data-disabled:hover:bg-secondary data-popup-open:bg-secondary-hover",
        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-hover data-popup-open:bg-muted-hover",
        secondary:
          "bg-secondary text-secondary-foreground not-data-disabled:hover:bg-secondary-hover data-popup-open:bg-secondary-hover",
      },
    },
  },
);

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:

PropTypeDefaultDescription
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.
loadingbooleanfalseWhen true, shows a spinner and disables the button.