Button
Displays a button or a component that looks like a button with extensive customization options.
Installation
Download the Button component using the Frontfriend CLI:
npx frontfriend download button
This will install the Button component along with its dependencies (Icon, Spinner components).
Usage
```tsx
import { Button } from '@/components/ui/button';
export default function Demo() {
return <Button variant="main">Click me</Button>;
}
```
```vue
<script setup>
import { Button } from '@/components/ui/button';
</script>
<template>
<Button variant="main">Click me</Button>
</template>
```
API Reference
Props
| Prop | Type | Default | Description |
|------|------|---------|-------------|
| `variant` | `'main' \| 'destructive' \| 'tertiary' \| 'secondary' \| 'ghost' \| 'ghostmain' \| 'link' \| 'linkDestructive'` | `'main'` | Visual style variant |
| `size` | `'default' \| 'sm' \| 'lg'` | `'default'` | Button size |
| `icon` | `keyof typeof icons` | `undefined` | Icon to display |
| `iconPosition` | `'prefix' \| 'suffix' \| 'default'` | `'default'` | Icon position. Use 'default' for icon-only buttons |
| `loading` | `boolean` | `false` | Show loading spinner |
| `loadingText` | `string` | `undefined` | Custom text to show during loading |
| `asChild` | `boolean` | `false` | Compose with child element using Radix Slot |
| ...rest | `React.ButtonHTMLAttributes` | - | All standard button HTML attributes |
| Prop | Type | Default | Description |
|------|------|---------|-------------|
| `variant` | `'main' \| 'destructive' \| 'tertiary' \| 'secondary' \| 'ghost' \| 'ghostmain' \| 'link' \| 'linkDestructive'` | `'main'` | Visual style variant |
| `size` | `'default' \| 'sm' \| 'lg'` | `'default'` | Button size |
| `icon` | `IconName` | `undefined` | Icon to display |
| `iconPosition` | `'prefix' \| 'suffix' \| 'default'` | `'default'` | Icon position. Use 'default' for icon-only buttons |
| `loading` | `boolean` | `false` | Show loading spinner |
| `loadingText` | `string` | `undefined` | Custom text to show during loading |
| `as` | `keyof HTMLElementTagNameMap` | `'button'` | HTML element to render as |
| `asChild` | `boolean` | `false` | Compose with child element using Radix Primitive |
| `label` | `string` | `'Button'` | Fallback text when no slot content provided |
| `class` | `string` | `undefined` | Additional CSS classes |
| ...rest | `ButtonHTMLAttributes` | - | All standard button HTML attributes |
Examples
Variants
The Button component supports 8 different visual variants to match different use cases.
Historical interactive example. See the current Button reference for live examples.
```tsx
import { Button } from '@/components/ui/button';
export default function VariantsDemo() {
return (
<div className="flex flex-wrap gap-4">
<Button variant="main">Main</Button>
<Button variant="secondary">Secondary</Button>
<Button variant="tertiary">Tertiary</Button>
<Button variant="destructive">Destructive</Button>
<Button variant="ghost">Ghost</Button>
<Button variant="ghostmain">Ghost Main</Button>
<Button variant="link">Link</Button>
<Button variant="linkDestructive">Link Destructive</Button>
</div>
);
}
```
```vue
<script setup>
import { Button } from '@/components/ui/button';
</script>
<template>
<div class="flex flex-wrap gap-4">
<Button variant="main">Main</Button>
<Button variant="secondary">Secondary</Button>
<Button variant="tertiary">Tertiary</Button>
<Button variant="destructive">Destructive</Button>
<Button variant="ghost">Ghost</Button>
<Button variant="ghostmain">Ghost Main</Button>
<Button variant="link">Link</Button>
<Button variant="linkDestructive">Link Destructive</Button>
</div>
</template>
```
Sizes
Three size options are available: small, default, and large.
Historical interactive example. See the current Button reference for live examples.
```tsx
import { Button } from '@/components/ui/button';
export default function SizesDemo() {
return (
<div className="flex items-center gap-4">
<Button size="sm">Small</Button>
<Button size="default">Default</Button>
<Button size="lg">Large</Button>
</div>
);
}
```
```vue
<script setup>
import { Button } from '@/components/ui/button';
</script>
<template>
<div class="flex items-center gap-4">
<Button size="sm">Small</Button>
<Button size="default">Default</Button>
<Button size="lg">Large</Button>
</div>
</template>
```
With Icons
Buttons can display icons in three different positions: prefix (before text), suffix (after text), or default (icon-only).
Icon Prefix
Historical interactive example. See the current Button reference for live examples.
```tsx
import { Button } from '@/components/ui/button';
export default function IconPrefixDemo() {
return (
<Button icon="Mail" iconPosition="prefix">
Send Email
</Button>
);
}
```
```vue
<script setup>
import { Button } from '@/components/ui/button';
</script>
<template>
<Button icon="Mail" iconPosition="prefix">
Send Email
</Button>
</template>
```
Icon Suffix
Historical interactive example. See the current Button reference for live examples.
```tsx
import { Button } from '@/components/ui/button';
export default function IconSuffixDemo() {
return (
<Button icon="ChevronRight" iconPosition="suffix">
Next
</Button>
);
}
```
```vue
<script setup>
import { Button } from '@/components/ui/button';
</script>
<template>
<Button icon="ChevronRight" iconPosition="suffix">
Next
</Button>
</template>
```
Icon Only
Historical interactive example. See the current Button reference for live examples.
```tsx
import { Button } from '@/components/ui/button';
export default function IconOnlyDemo() {
return (
<div className="flex gap-2">
<Button icon="Settings" iconPosition="default" aria-label="Settings" />
<Button icon="Search" iconPosition="default" aria-label="Search" />
<Button icon="Heart" iconPosition="default" aria-label="Favorite" />
</div>
);
}
```
```vue
<script setup>
import { Button } from '@/components/ui/button';
</script>
<template>
<div class="flex gap-2">
<Button icon="Settings" iconPosition="default" aria-label="Settings" />
<Button icon="Search" iconPosition="default" aria-label="Search" />
<Button icon="Heart" iconPosition="default" aria-label="Favorite" />
</div>
</template>
```
Loading State
Display a loading spinner and optional loading text while an async action is in progress.
Historical interactive example. See the current Button reference for live examples.
```tsx
import { Button } from '@/components/ui/button';
import { useState } from 'react';
export default function LoadingDemo() {
const [isLoading, setIsLoading] = useState(false);
const handleClick = async () => {
setIsLoading(true);
await new Promise(resolve => setTimeout(resolve, 2000));
setIsLoading(false);
};
return (
<div className="flex gap-4">
<Button loading={isLoading} onClick={handleClick}>
Submit
</Button>
<Button
loading={isLoading}
loadingText="Saving..."
onClick={handleClick}
>
Save Changes
</Button>
</div>
);
}
```
```vue
<script setup>
import { Button } from '@/components/ui/button';
import { ref } from 'vue';
const isLoading = ref(false);
const handleClick = async () => {
isLoading.value = true;
await new Promise(resolve => setTimeout(resolve, 2000));
isLoading.value = false;
};
</script>
<template>
<div class="flex gap-4">
<Button :loading="isLoading" @click="handleClick">
Submit
</Button>
<Button
:loading="isLoading"
loadingText="Saving..."
@click="handleClick"
>
Save Changes
</Button>
</div>
</template>
```
As Child (Composition)
Use the asChild prop to compose the Button with other components, such as links.
Historical interactive example. See the current Button reference for live examples.
```tsx
import { Button } from '@/components/ui/button';
import Link from 'next/link';
export default function AsChildDemo() {
return (
<Button asChild>
<Link href="/dashboard">
Go to Dashboard
</Link>
</Button>
);
}
```
```vue
<script setup>
import { Button } from '@/components/ui/button';
import { RouterLink } from 'vue-router';
</script>
<template>
<Button asChild>
<RouterLink to="/dashboard">
Go to Dashboard
</RouterLink>
</Button>
</template>
```
Disabled State
Historical interactive example. See the current Button reference for live examples.
```tsx
import { Button } from '@/components/ui/button';
export default function DisabledDemo() {
return (
<div className="flex gap-4">
<Button disabled>Disabled Button</Button>
<Button variant="destructive" disabled>
Can't Delete
</Button>
</div>
);
}
```
```vue
<script setup>
import { Button } from '@/components/ui/button';
</script>
<template>
<div class="flex gap-4">
<Button disabled>Disabled Button</Button>
<Button variant="destructive" :disabled="true">
Can't Delete
</Button>
</div>
</template>
```
Design Tokens
The Button component uses design tokens from @frontfriend/tailwind for consistent theming. All colors, spacing, and typography are controlled through the design token system, ensuring consistency across your application.
Accessibility
- Button includes proper ARIA attributes
- Icon-only buttons should include an
aria-labelfor screen readers - Disabled state is properly communicated to assistive technologies
- Loading state maintains button focus and communicates status
Related Components
- Icon: Used internally for displaying icons
- Spinner: Used for loading states