# Buttons & Actions

> Interactive button components and action triggers.

*Canonical: https://makerkit.dev/docs/nextjs-prisma/ui-components/buttons-and-actions*

---

Use `Button` from `@kit/ui/button` with variants (default, secondary, outline, ghost, destructive, link) and sizes (xs, sm, default, lg, icon). For loading states, disable the button and show a `Spinner`. Use the `render` prop to make buttons act as links.

This guide is part of the [UI Components](./overview) documentation.

**Definition:** Button components are interactive elements that trigger actions, with built-in support for variants, sizes, disabled states, and composition with other elements via the Base UI render prop pattern.

## Button

Primary button component with variants and sizes.

```typescript
import { Button } from '@kit/ui/button';
```

### Variants

```tsx
<Button variant="default">Default</Button>
<Button variant="secondary">Secondary</Button>
<Button variant="outline">Outline</Button>
<Button variant="ghost">Ghost</Button>
<Button variant="destructive">Destructive</Button>
<Button variant="link">Link</Button>
```

### Sizes

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

### Icon Buttons

```tsx
import { Plus, Settings } from 'lucide-react';

<Button size="icon">
  <Plus className="h-4 w-4" />
</Button>

<Button size="icon-sm">
  <Settings className="h-4 w-4" />
</Button>

<Button size="icon-xs">
  <Plus className="h-3 w-3" />
</Button>
```

### Loading State

```tsx
import { Spinner } from '@kit/ui/spinner';

<Button disabled={isPending}>
  {isPending ? (
    <>
      <Spinner className="mr-2 h-4 w-4" />
      Loading...
    </>
  ) : (
    'Submit'
  )}
</Button>
```

### As Link

Uses the `render` prop pattern from Base UI:

```tsx
import Link from 'next/link';

<Button render={<Link href="/dashboard" />}>
  Go to Dashboard
</Button>
```

## Button Group

Group related buttons together.

```typescript
import { ButtonGroup } from '@kit/ui/button-group';
```

```tsx
<ButtonGroup>
  <Button variant="outline">Left</Button>
  <Button variant="outline">Center</Button>
  <Button variant="outline">Right</Button>
</ButtonGroup>
```

### Vertical Orientation

```tsx
<ButtonGroup orientation="vertical">
  <Button variant="outline">Top</Button>
  <Button variant="outline">Middle</Button>
  <Button variant="outline">Bottom</Button>
</ButtonGroup>
```

## Card Button

Clickable card component for selection interfaces.

```typescript
import { CardButton, CardButtonHeader, CardButtonTitle, CardButtonContent, CardButtonFooter } from '@kit/ui/card-button';
```

```tsx
<CardButton onClick={handleClick}>
  <CardButtonHeader>
    <CardButtonTitle>Option Title</CardButtonTitle>
  </CardButtonHeader>
  <CardButtonContent>
    <span className="text-muted-foreground text-sm">
      Description of this option
    </span>
  </CardButtonContent>
</CardButton>
```

### Without Arrow

```tsx
<CardButton>
  <CardButtonHeader displayArrow={false}>
    <CardButtonTitle>Simple Card</CardButtonTitle>
  </CardButtonHeader>
  <CardButtonContent>
    Content without arrow indicator
  </CardButtonContent>
</CardButton>
```

## Copy to Clipboard

Button that copies text to clipboard with toast feedback.

```typescript
import { CopyToClipboard } from '@kit/ui/copy-to-clipboard';
```

```tsx
<CopyToClipboard value="text-to-copy">
  Copy this text
</CopyToClipboard>

{/* Custom messages */}
<CopyToClipboard
  value={apiKey}
  successMessage="API key copied!"
  errorMessage="Failed to copy API key"
>
  {apiKey}
</CopyToClipboard>
```

Shows a copy icon that changes to a checkmark on success, with toast notification.

## Common Pitfalls

- **Not disabling during loading**: Always set `disabled={isPending}` when showing a loading state to prevent double submissions.
- **Wrong Link integration**: Use `render={<Link href="..." />}` pattern, not wrapping Button with Link.
- **Missing icon sizing**: Icons in buttons need explicit sizing: `<Plus className="h-4 w-4" />`.
- **ButtonGroup spacing**: Don't add gap classes to ButtonGroup - it handles spacing internally.
- **Destructive without confirmation**: Always pair `variant="destructive"` with a confirmation dialog for irreversible actions.

{% faq
   title="Frequently Asked Questions"
   items=[
     {"question": "How do I make a button look like a link?", "answer": "Use variant='link'. For actual navigation, combine with the render prop: <Button variant='link' render={<Link href='...' />}>"},
     {"question": "What's the difference between ghost and outline?", "answer": "Ghost has no border and only shows background on hover. Outline has a visible border and changes background on hover."},
     {"question": "How do I add an icon to a button?", "answer": "Import from lucide-react and place inside the button: <Button><Plus className='h-4 w-4 mr-2' />Add Item</Button>"},
     {"question": "Can I use buttons in Server Components?", "answer": "Static buttons (without onClick) work in Server Components. Buttons with event handlers need 'use client'."},
     {"question": "How do I show loading state during form submission?", "answer": "Use useTransition or form pending state: disable the button and conditionally render Spinner when isPending is true."}
   ]
/%}

---

**Next:** [Layout & Structure →](./layout-and-structure)
