Skip to main content
A flexible button component that supports multiple visual variants, sizes, and can render as a child component using the asChild pattern.

Installation

The Button component is built with:
  • class-variance-authority for variant management
  • radix-ui Slot for composition
  • Custom utility functions from @/lib/utils

Basic Usage

import { Button } from "@/components/ui/button";

export default function Example() {
  return <Button>Click me</Button>;
}

Variants

The Button component supports 6 visual variants:
<Button variant="default">
  Primary Action
</Button>
Primary variant with background color and high contrast.

Sizes

<Button size="xs">Extra Small</Button>
<Button size="sm">Small</Button>
<Button size="default">Default</Button>
<Button size="lg">Large</Button>

Real-World Examples

Hero CTA Buttons

From modules/landing/components/Hero.tsx:55-67:
<Button
  className="h-14 w-full rounded-2xl bg-[#2563eb] px-10 text-[16px] font-bold text-white transition-all hover:bg-[#2563eb]/90 hover:shadow-xl hover:shadow-blue-200 sm:w-auto"
  asChild
>
  <Link href="/register">Comenzar prueba gratuita</Link>
</Button>

<Button
  variant="outline"
  className="h-14 w-full rounded-2xl border-zinc-200 bg-white px-10 text-[16px] font-bold text-zinc-700 transition-all hover:bg-zinc-50 sm:w-auto"
  asChild
>
  <Link href="/demo">Ver demostración</Link>
</Button>
From components/layout/navbar.tsx:48-52:
<Button variant="ghost" asChild>
  <Link href="/login">Iniciar sesión</Link>
</Button>

<Button className="bg-blue-600 text-white rounded-xl px-6" asChild>
  <Link href="/register">Comenzar gratis</Link>
</Button>

Form Submit Button

From modules/auth/components/LoginForm.tsx:106-112:
<Button
  type="submit"
  className="w-full py-6 bg-blue-600 hover:bg-blue-700 text-lg font-bold"
  disabled={isPending}
>
  {isPending ? "Ingresando..." : "Ingresar"}
</Button>

Password Toggle Button

From modules/auth/components/LoginForm.tsx:77-85:
<Button
  type="button"
  variant="ghost"
  size="sm"
  className="absolute right-2 top-1/2 -translate-y-1/2"
  onClick={() => setShowPassword(!showPassword)}
>
  {showPassword ? <EyeOffIcon className="h-4 w-4" /> : <EyeIcon className="h-4 w-4" />}
</Button>

Props

variant
string
default:"default"
Visual style variantOptions: default | outline | secondary | ghost | destructive | link
size
string
default:"default"
Button size presetOptions: xs | sm | default | lg | icon-xs | icon-sm | icon | icon-lg
asChild
boolean
default:"false"
When true, the button will render its child component instead of a <button> element. Useful for wrapping Link components.
className
string
Additional CSS classes to apply to the button
disabled
boolean
default:"false"
Disables the button and applies disabled styles (50% opacity, pointer-events disabled)
type
string
default:"button"
HTML button typeOptions: button | submit | reset

Accessibility

  • Includes focus-visible styles with ring and border for keyboard navigation
  • Supports aria-invalid state with destructive styling
  • Automatically handles disabled state with appropriate styling
  • Icon-only buttons should include aria-label for screen readers

Styling Details

  • Base height: 32px (h-8) for default size
  • Border radius: rounded-lg (8px)
  • Focus ring: 3px ring with 50% opacity
  • Transition: All properties with transition-all
  • SVG icons: Automatically sized to 16px (size-4) unless overridden
  • Group context: Uses group/button for nested element styling

TypeScript

type ButtonProps = React.ComponentProps<"button"> & 
  VariantProps<typeof buttonVariants> & {
    asChild?: boolean;
  };
The component extends all native HTML button attributes while adding variant control and composition capabilities.

Build docs developers (and LLMs) love