Devie UI

The Button component extends Base UI's Button. It adds variant, size, loading state, and icon props for common use cases.

Installation

Here is the component implementation code that you can copy to your project:

// https://base-ui.com/react/components/button
 
import { Button as BaseButton } from "@base-ui-components/react/button";
import clsx from "clsx";
import styles from "./Button.module.scss";
 
type Variant =
  | "primary"
  | "secondary"
  | "danger"
  | "naked"
  | "icon-primary"
  | "icon-secondary"
  | "icon-danger"
  | "icon-naked";
 
type Props = BaseButton.Props & {
  variant?: Variant;
  size?: "sm" | "md" | "xl";
  isLoading?: boolean;
};
 
const capitalize = (s: string) => s.charAt(0).toUpperCase() + s.slice(1);
 
export default function Button({
  variant = "primary",
  size = "md",
  children,
  className,
  disabled,
  isLoading,
  ...props
}: Props) {
  const isIcon = variant.startsWith("icon-");
  const baseVariant = isIcon ? variant.slice(5) : variant;
 
  return (
    <BaseButton
      className={clsx(
        styles.button,
        styles[`variant${capitalize(baseVariant)}`],
        styles[`size${capitalize(size)}`],
        isIcon && styles.icon,
        className,
      )}
      data-loading={isLoading}
      data-variant={baseVariant}
      disabled={disabled || isLoading}
      focusableWhenDisabled={isLoading}
      {...props}
    >
      {children}
      {isLoading && (
        <div className={styles.loadingOverlay}>
          <div
            className={clsx(styles.loader, styles[`loader${capitalize(baseVariant)}`])}
          />
        </div>
      )}
    </BaseButton>
  );
}

Use Cases

Button with different variants

The Button component offers four distinct variants to fit different UI needs: primary for main call-to-action buttons, secondary for less visually dominant actions, danger for destructive actions, and naked for minimal buttons without background.

Button with different sizes

Buttons can be sized using the size prop with three options: sm for compact buttons, md for the default medium size, and xl for larger, more prominent buttons.

Loading state of the button

The isLoading prop provides a CSS-only loader state that maintains the button's original dimensions to prevent layout shifts during loading transitions. This ensures a smooth UI experience, especially in forms or button groups where sudden width changes could disrupt the layout.

Disabled state of the buttons

Buttons can be disabled using the standard disabled prop. When disabled, buttons show a reduced opacity and are not interactive, providing clear visual feedback to users.

Icon-only buttons

The icon prop creates a square button with equal padding on all sides, perfect for icon-only buttons. This works with all variants and sizes.