- Primary
- Secondary
- Accent
- Destructive
- Ghost
- Outline
- Accent background color (
bg-primary) - White text (
text-primary-foreground) - Hover state with darker background
- Focus ring with primary color
- Subtle shadow that increases on hover
Learn more about Mintlify
Enter your email to receive updates about new features and product releases.
Interactive button component with multiple variants, sizes, loading states, and icon support
type ButtonVariant = 'primary' | 'secondary' | 'accent' | 'destructive' | 'ghost' | 'outline';
type ButtonSize = 'sm' | 'md' | 'lg';
interface ButtonProps extends ButtonHTMLAttributes<HTMLButtonElement> {
variant?: ButtonVariant;
size?: ButtonSize;
isLoading?: boolean;
leftIcon?: React.ReactNode;
rightIcon?: React.ReactNode;
}
primary - Primary action with accent color and shadowsecondary - Secondary action with muted stylingaccent - Highlighted action with accent colordestructive - Dangerous or delete actions with red stylingghost - Transparent background, visible on hoveroutline - Transparent with border, fills on hoversm - Small (px-3 py-1.5 text-sm)md - Medium (px-4 py-2 text-base)lg - Large (px-6 py-3 text-lg)isLoading is true.isLoading is true.isLoading is true.ButtonHTMLAttributes<HTMLButtonElement>, so you can use props like onClick, type, form, etc.<Button variant="primary">Primary Button</Button>
bg-primary)text-primary-foreground)<Button variant="secondary">Secondary Button</Button>
bg-secondary)<Button variant="accent">Accent Button</Button>
<Button variant="destructive">Delete</Button>
bg-destructive)<Button variant="ghost">Ghost Button</Button>
<Button variant="outline">Outline Button</Button>
border-2)<Button size="sm">Small Button</Button>
<Button size="md">Medium Button</Button>
<Button size="lg">Large Button</Button>
px-3 py-1.5 text-sm gap-1.5px-4 py-2 text-base gap-2px-6 py-3 text-lg gap-2.5import { Button } from '@/components/Button';
import { useState } from 'react';
function SubmitButton() {
const [isLoading, setIsLoading] = useState(false);
const handleSubmit = async () => {
setIsLoading(true);
try {
await submitForm();
} finally {
setIsLoading(false);
}
};
return (
<Button isLoading={isLoading} onClick={handleSubmit}>
Submit Form
</Button>
);
}
isLoading is true:
import { ArrowLeft } from 'lucide-react';
<Button leftIcon={<ArrowLeft size={16} />}>
Go Back
</Button>
import { ArrowRight } from 'lucide-react';
<Button rightIcon={<ArrowRight size={16} />}>
Continue
</Button>
import { Search } from 'lucide-react';
<Button variant="ghost" leftIcon={<Search size={18} />}>
{/* Empty children for icon-only button */}
</Button>
flex-shrink-0 span to prevent them from shrinking when the button text is long.import { Button } from '@/components/Button';
import { ArrowRight } from 'lucide-react';
function ButtonExample() {
return (
<div className="flex flex-wrap gap-3">
{/* Variants */}
<Button variant="primary">Primary</Button>
<Button variant="secondary">Secondary</Button>
<Button variant="accent">Accent</Button>
<Button variant="destructive">Delete</Button>
<Button variant="ghost">Ghost</Button>
<Button variant="outline">Outline</Button>
{/* Sizes */}
<Button size="sm">Small</Button>
<Button size="md">Medium</Button>
<Button size="lg">Large</Button>
{/* States */}
<Button isLoading>Loading</Button>
<Button disabled>Disabled</Button>
{/* With Icons */}
<Button leftIcon={<ArrowRight size={16} />}>
With Icon
</Button>
</div>
);
}
<button> elementUse appropriate variants for action hierarchy
primary for the main call-to-actionsecondary for supporting actionsdestructive only for dangerous or irreversible actionsghost or outline for tertiary actionsProvide clear button text
Handle loading states properly
isLoading during async operations to prevent duplicate submissions and provide visual feedback.Icon size consistency