Components
Button
A versatile button component with multiple variants, sizes, and states for user interactions.
import { Button } from '@/components/ui/button';
export function ButtonDemo() {
return (
<div className='flex flex-col gap-4'>
<div className='flex items-center gap-3'>
<Button>Default Button</Button>
</div>
</div>
);
}Installation
CLI
npx shadcn@latest add "https://maksud.dev/r/button"Manual
Install the following dependencies:
npm install @radix-ui/react-slot class-variance-authorityCopy and paste the following code into your project.
import { Slot } from '@radix-ui/react-slot';
import { cva, type VariantProps } from 'class-variance-authority';
import type * as React from 'react';
import { cn } from '@/lib/utils';
const buttonVariants = cva(
"inline-flex shrink-0 items-center justify-center gap-2 whitespace-nowrap rounded-md font-medium text-sm outline-none ring-offset-transparent transition-all focus-visible:border-background focus-visible:ring-[3px] focus-visible:ring-ring/50 disabled:pointer-events-none disabled:opacity-50 aria-invalid:border-destructive aria-invalid:ring-destructive/20 dark:aria-invalid:ring-destructive/40 [&_svg:not([class*='size-'])]:size-4 [&_svg]:pointer-events-none [&_svg]:shrink-0",
{
variants: {
variant: {
default:
'border-primary/20 border-x border-b bg-gradient-to-b from-primary/80 to-primary text-primary-foreground shadow focus:ring-3 focus:ring-primary/50 focus:ring-offset-1 active:opacity-90',
destructive:
'border-destructive/20 border-x border-b bg-gradient-to-b from-destructive/80 to-destructive text-destructive-foreground shadow focus:ring-3 focus:ring-destructive/50 focus:ring-offset-1 active:opacity-90',
warning:
'border-attention/20 border-x border-b bg-gradient-to-b from-attention/80 to-attention text-attention-foreground shadow focus:ring-3 focus:ring-attention/50 focus:ring-offset-1 active:opacity-90',
outline:
'border border-input bg-gradient-to-b from-background to-background/80 text-foreground shadow hover:bg-accent/50 focus:ring-3 focus:ring-ring/50 focus:ring-offset-1 active:opacity-90',
secondary:
'border-secondary/20 border-x border-b bg-gradient-to-b from-secondary/80 to-secondary text-secondary-foreground shadow focus:ring-3 focus:ring-accent focus:ring-offset-1 active:opacity-90',
ghost:
'text-foreground hover:bg-gradient-to-b hover:from-accent hover:to-accent/80 focus:ring-3 focus:ring-ring/50 focus:ring-offset-1 active:opacity-90',
link: 'text-primary underline-offset-4 hover:underline focus:ring-3 focus:ring-primary/50 active:opacity-90',
success:
'border-success border-b bg-gradient-to-b from-success/80 to-success text-white shadow focus:ring-3 focus:ring-success/50 focus:ring-offset-1 active:opacity-90',
},
size: {
default: 'h-8 rounded-xl px-3 py-2 has-[>svg]:px-3',
sm: 'h-7 gap-1.5 rounded-xl px-2.5 has-[>svg]:px-2.5',
lg: 'h-10 rounded-xl px-6 text-base has-[>svg]:px-4',
icon: 'size-9 rounded-xl',
pill: "h-7 rounded-full px-2.5 has-[>svg]:px-2.5 [&_svg:not([class*='size-'])]:size-3",
},
},
defaultVariants: {
variant: 'default',
size: 'default',
},
}
);
interface ButtonProps extends React.ComponentProps<'button'>, VariantProps<typeof buttonVariants> {
asChild?: boolean;
loading?: boolean;
}
function Button({
className,
variant,
size,
asChild = false,
loading = false,
children,
disabled,
...props
}: ButtonProps) {
const Comp = asChild ? Slot : 'button';
return (
<Comp
data-slot='button'
className={cn(buttonVariants({ variant, size, className }))}
disabled={disabled || loading}
{...props}
>
{/* {loading && <Loader2 className='mr-0.5 h-4 w-4 animate-spin' />} */}
{children}
</Comp>
);
}
export { Button, buttonVariants };Layout
Import the component and use it in your application.
import { Button } from "@/components/ui/button";
export default function Example() {
return (
<Button>Click me</Button>
);
}Examples
Variants
import { Button } from '@/components/ui/button';
export function ButtonVariantsDemo() {
return (
<div className='grid grid-cols-2 gap-4 md:grid-cols-4'>
<Button variant='default'>Default</Button>
<Button variant='destructive'>Destructive</Button>
<Button variant='warning'>Warning</Button>
<Button variant='outline'>Outline</Button>
<Button variant='secondary'>Secondary</Button>
<Button variant='ghost'>Ghost</Button>
<Button variant='link'>Link</Button>
<Button variant='success'>Success</Button>
</div>
);
}The Button component supports eight different variants: default, destructive, warning, outline, secondary, ghost, link, and success.
Sizes
import { Button } from '@/components/ui/button';
import { Download } from 'lucide-react';
export function ButtonSizesDemo() {
return (
<div className='flex flex-col gap-6'>
<div className='flex items-center gap-3'>
<Button size='sm'>Small</Button>
<Button size='default'>Default</Button>
<Button size='lg'>Large</Button>
</div>
<div className='flex items-center gap-3'>
<Button size='icon'>
<Download className='size-4' />
</Button>
<Button size='pill'>Pill Button</Button>
</div>
</div>
);
}Choose from five different sizes: default, sm, lg, icon, and pill.
With Icons
import { Button } from '@/components/ui/button';
import { Download, Mail, Plus, Settings } from 'lucide-react';
export function ButtonWithIconsDemo() {
return (
<div className='flex flex-col gap-4'>
<div className='flex items-center gap-3'>
<Button>
<Mail className='size-4' />
Send Email
</Button>
<Button variant='outline'>
<Download className='size-4' />
Download
</Button>
<Button variant='secondary'>
<Settings className='size-4' />
Settings
</Button>
</div>
<div className='flex items-center gap-3'>
<Button size='icon' variant='default'>
<Plus className='size-4' />
</Button>
<Button size='icon' variant='outline'>
<Download className='size-4' />
</Button>
<Button size='icon' variant='ghost'>
<Settings className='size-4' />
</Button>
</div>
</div>
);
}Buttons can include icons to provide additional visual context and improve usability.
Loading State
import { Button } from '@/components/ui/button';
import { Download, Save } from 'lucide-react';
export function ButtonLoadingDemo() {
return (
<div className='flex flex-col gap-4'>
<div className='flex items-center gap-3'>
<Button loading>Loading</Button>
<Button variant='outline' loading>
Please wait
</Button>
<Button variant='secondary' loading>
<Save className='size-4' />
Saving...
</Button>
</div>
<div className='flex items-center gap-3'>
<Button disabled>Disabled</Button>
<Button variant='outline' disabled>
<Download className='size-4' />
Download
</Button>
</div>
</div>
);
}Use the loading prop to show a loading state and disable user interaction.
API Reference
| Prop | Description |
|---|---|
variant | The visual style variant of the button. |
size | The size of the button. |
asChild | When true, renders as a child component (e.g., Link) instead of a button element. |
loading | When true, shows loading state and disables the button. |
disabled | When true, disables the button and prevents user interaction. |
Accessibility
- The button uses semantic
<button>element by default - Supports all standard button HTML attributes including
disabled - Proper focus management with visible focus indicators
- Screen reader accessible with appropriate ARIA attributes
- Loading and disabled states are properly communicated to assistive technologies