Skip to main content

button

A customizable button component for triggering actions in your application.

Philosophy

Buttons are the primary call to action in any interface. We ship an intentionally rich variant system because buttons carry the most visual weight in UI decisions - primary, destructive, ghost, and outline each communicate different levels of commitment. The asChild pattern lets you render any element as a button, because sometimes a link needs to look like one.

  • Multiple styles, sizes, and border options out of the box
  • Built-in loading state with spinner and auto-disable
  • Supports icons, dual icons, and collapsed icon-only mode
  • Flexible asChild rendering for links and custom wrappers
  • Smooth hover animations with optional AnimationIcon
  • Fully typed, accessible, and responsive
  • Powered by @gentleduck/variants for scalable theming
  • Clean, composable, and Tailwind-optimized

How It's Built

Loading diagram...

Installation


npx @gentleduck/cli add button

npx @gentleduck/cli add button

Usage

import { Button } from '@/components/ui/button'
import { Button } from '@/components/ui/button'
<Button>Button</Button>
<Button>Button</Button>

Button

Button is a versatile and customizable React component designed to render a button with various styles and functionalities. It supports different sizes, variants, and additional features like loading states and icons. The component is flexible and can be used in a variety of scenarios, from simple buttons to complex interactive elements.

Example Usage:

import { Button } from '@/components/ui/button'
import { Tooltip, TooltipContent, TooltipTrigger } from '@/components/ui/tooltip'
 
const MyButton = () => {
  return (
    <Tooltip>
      <TooltipTrigger asChild>
        <Button size="lg" variant="default">
          Submit
        </Button>
      </TooltipTrigger>
      <TooltipContent>Submit Button</TooltipContent>
    </Tooltip>
  )
}
import { Button } from '@/components/ui/button'
import { Tooltip, TooltipContent, TooltipTrigger } from '@/components/ui/tooltip'
 
const MyButton = () => {
  return (
    <Tooltip>
      <TooltipTrigger asChild>
        <Button size="lg" variant="default">
          Submit
        </Button>
      </TooltipTrigger>
      <TooltipContent>Submit Button</TooltipContent>
    </Tooltip>
  )
}

You can use the buttonVariants helper to create any element like a link that looks like a button.

import { buttonVariants } from '@/components/ui/button'
import { buttonVariants } from '@/components/ui/button'
<Link className={buttonVariants({ variant: 'outline' })}>I'm a Link 🦆</Link>
<Link className={buttonVariants({ variant: 'outline' })}>I'm a Link 🦆</Link>

Alternatively, you can set the asChild parameter and nest the link component.

<Button asChild>
  <Link href="/tos">I'm a Link to ToS 🦆</Link>
</Button>
<Button asChild>
  <Link href="/tos">I'm a Link to ToS 🦆</Link>
</Button>

Examples

Primary

Secondary

Ghost

Outline

Destructive

Warning

Dashed

Nothing

With Border

With Border Destructive

With Border Warning

With Border Secondary

With Icon

Loading

When loading is true, Button renders a lucide-react <Loader /> with animate-spin, disables interaction, and replaces the leading icon until loading completes.

With Second Icon

Collapsible

Expand Icon

RTL Support

Motion

Use MotionButton for subtle press feedback powered by motion. The button scales to 0.97 on tap for a physical press feel.

API Reference

Button

PropTypeDefaultDescription
variant'default' | 'destructive' | 'warning' | 'outline' | 'dashed' | 'secondary' | 'ghost' | 'link' | 'expandIcon' | 'nothing''default'Visual style variant
size'default' | 'sm' | 'lg' | 'icon' | 'icon-sm' | 'icon-lg''default'Size of the button
border'default' | 'primary' | 'secondary' | 'destructive' | 'warning''default'Border style variant
asChildboolean-If true, renders using a custom child element via Slot (e.g. a Link)
loadingboolean-If true, renders <Loader className="animate-spin" />, disables the button, and temporarily overrides icon
disabledboolean-Disables the button manually
iconReact.ReactNode-Primary icon displayed before the content (or centered if collapsed)
secondIconReact.ReactNode-Secondary icon displayed after the content
isCollapsedboolean-If true, renders icon-only button (hides children and secondIcon)
type'button' | 'submit' | 'reset''button'Native HTML button type
...propsOmit<React.HTMLProps<HTMLButtonElement>, 'size'>-Additional props to spread to the button element

AnimationIcon

PropTypeDefaultDescription
childrenReact.ReactNode(required)Content inside the animation icon wrapper
animationIcon{ icon?: React.ReactNode; iconPlacement?: 'left' | 'right' }-Animation icon configuration with icon element and placement direction

MotionButton

Adds whileTap press feedback (scale 0.97) and animated icon/text transitions via AnimatePresence. asChild is not supported. Requires the motion package.

PropTypeDefaultDescription
...propsOmit<ButtonProps, 'asChild'>-All props from Button except asChild