---
title: Button
description: Displays a button or a component that looks like a button.
---

```svelte
<script lang="ts">
  import ArrowUpIcon from '@lucide/svelte/icons/arrow-up';
  import { Button } from '#lib/components/ui/button';
</script>

<div class="flex flex-wrap items-center gap-2 md:flex-row">
  <Button variant="outline">Button</Button>
  <Button variant="outline" size="icon" aria-label="Submit">
    <ArrowUpIcon />
  </Button>
</div>
```

## Installation

<InstallTabs>

<InstallTab value="cli">

```bash
npx @sveltery/ui@latest add button
```

</InstallTab>

<InstallTab value="manual">

<Steps>

### Install the dependencies

```bash
npm install @sveltery/base
```

### Copy the component

Copy the following files into `src/lib/components/ui/button`.

<ComponentSource name="button" />

### Update the import paths

The component folders import each other with relative paths, so keep them side by side. If you place them elsewhere, change the imports to match your project setup.

</Steps>

</InstallTab>

</InstallTabs>

## Usage

```svelte
<script lang="ts">
  import { Button } from '#lib/components/ui/button';
</script>
```

```svelte
<Button variant="outline">Button</Button>
```

## Cursor

Tailwind v4 [switched](https://tailwindcss.com/docs/upgrade-guide#buttons-use-the-default-cursor) from `cursor: pointer` to `cursor: default` for the button component.

If you want to keep the `cursor: pointer` behavior, add the following code to your CSS file:

```css showLineNumbers title="src/app.css"
@layer base {
  button:not(:disabled),
  [role="button"]:not(:disabled) {
    cursor: pointer;
  }
}
```

## Size

Use the `size` prop to change the size of the button.

```svelte
<script lang="ts">
  import ArrowUpRightIcon from '@lucide/svelte/icons/arrow-up-right';
  import { Button } from '#lib/components/ui/button';
</script>

<div class="flex flex-col items-start gap-8 sm:flex-row">
  <div class="flex items-start gap-2">
    <Button size="xs" variant="outline">Extra Small</Button>
    <Button size="icon-xs" aria-label="Submit" variant="outline">
      <ArrowUpRightIcon />
    </Button>
  </div>
  <div class="flex items-start gap-2">
    <Button size="sm" variant="outline">Small</Button>
    <Button size="icon-sm" aria-label="Submit" variant="outline">
      <ArrowUpRightIcon />
    </Button>
  </div>
  <div class="flex items-start gap-2">
    <Button variant="outline">Default</Button>
    <Button size="icon" aria-label="Submit" variant="outline">
      <ArrowUpRightIcon />
    </Button>
  </div>
  <div class="flex items-start gap-2">
    <Button variant="outline" size="lg">Large</Button>
    <Button size="icon-lg" aria-label="Submit" variant="outline">
      <ArrowUpRightIcon />
    </Button>
  </div>
</div>
```

## Default

```svelte
<script lang="ts">
  import { Button } from '#lib/components/ui/button';
</script>

<Button>Button</Button>
```

## Outline

```svelte
<script lang="ts">
  import { Button } from '#lib/components/ui/button';
</script>

<Button variant="outline">Outline</Button>
```

## Secondary

```svelte
<script lang="ts">
  import { Button } from '#lib/components/ui/button';
</script>

<Button variant="secondary">Secondary</Button>
```

## Ghost

```svelte
<script lang="ts">
  import { Button } from '#lib/components/ui/button';
</script>

<Button variant="ghost">Ghost</Button>
```

## Destructive

```svelte
<script lang="ts">
  import { Button } from '#lib/components/ui/button';
</script>

<Button variant="destructive">Destructive</Button>
```

## Link

```svelte
<script lang="ts">
  import { Button } from '#lib/components/ui/button';
</script>

<Button variant="link">Link</Button>
```

## Icon

```svelte
<script lang="ts">
  import CircleFadingArrowUpIcon from '@lucide/svelte/icons/circle-fading-arrow-up';
  import { Button } from '#lib/components/ui/button';
</script>

<Button variant="outline" size="icon">
  <CircleFadingArrowUpIcon />
</Button>
```

## With Icon

Remember to add the `data-icon="inline-start"` or `data-icon="inline-end"` attribute to the icon for the correct spacing.

```svelte
<script lang="ts">
  import IconGitBranch from '@tabler/icons-svelte-runes/icons/git-branch';
  import IconGitFork from '@tabler/icons-svelte-runes/icons/git-fork';
  import { Button } from '#lib/components/ui/button';
</script>

<div class="flex gap-2">
  <Button variant="outline">
    <IconGitBranch data-icon="inline-start" /> New Branch
  </Button>
  <Button variant="outline">
    Fork
    <IconGitFork data-icon="inline-end" />
  </Button>
</div>
```

## Rounded

Use the `rounded-full` class to make the button rounded.

```svelte
<script lang="ts">
  import ArrowUpIcon from '@lucide/svelte/icons/arrow-up';
  import { Button } from '#lib/components/ui/button';
</script>

<div class="flex gap-2">
  <Button class="rounded-full">Get Started</Button>
  <Button variant="outline" size="icon" class="rounded-full">
    <ArrowUpIcon />
  </Button>
</div>
```

## Spinner

Render a `<Spinner />` component inside the button to show a loading state. Remember to add the `data-icon="inline-start"` or `data-icon="inline-end"` attribute to the spinner for the correct spacing.

```svelte
<script lang="ts">
  import { Button } from '#lib/components/ui/button';
  import { Spinner } from '#lib/components/ui/spinner';
</script>

<div class="flex gap-2">
  <Button variant="outline" disabled>
    <Spinner data-icon="inline-start" />
    Generating
  </Button>
  <Button variant="secondary" disabled>
    Downloading
    <Spinner data-icon="inline-start" />
  </Button>
</div>
```

## Button Group

To create a button group, use the `ButtonGroup` component. See the [Button Group](/docs/components/button-group) documentation for more details.

```svelte
<script lang="ts">
  import ArchiveIcon from '@lucide/svelte/icons/archive';
  import ArrowLeftIcon from '@lucide/svelte/icons/arrow-left';
  import CalendarPlusIcon from '@lucide/svelte/icons/calendar-plus';
  import ClockIcon from '@lucide/svelte/icons/clock';
  import ListFilterIcon from '@lucide/svelte/icons/list-filter';
  import MailCheckIcon from '@lucide/svelte/icons/mail-check';
  import MoreHorizontalIcon from '@lucide/svelte/icons/ellipsis';
  import TagIcon from '@lucide/svelte/icons/tag';
  import Trash2Icon from '@lucide/svelte/icons/trash';
  import { Button } from '#lib/components/ui/button';
  import { ButtonGroup } from '#lib/components/ui/button-group';
  import {
    DropdownMenu,
    DropdownMenuContent,
    DropdownMenuGroup,
    DropdownMenuItem,
    DropdownMenuRadioGroup,
    DropdownMenuRadioItem,
    DropdownMenuSeparator,
    DropdownMenuSub,
    DropdownMenuSubContent,
    DropdownMenuSubTrigger,
    DropdownMenuTrigger
  } from '#lib/components/ui/dropdown-menu';

  let label = $state('personal');
</script>

<ButtonGroup>
  <ButtonGroup class="hidden sm:flex">
    <Button variant="outline" size="icon" aria-label="Go Back">
      <ArrowLeftIcon />
    </Button>
  </ButtonGroup>
  <ButtonGroup>
    <Button variant="outline">Archive</Button>
    <Button variant="outline">Report</Button>
  </ButtonGroup>
  <ButtonGroup>
    <Button variant="outline">Snooze</Button>
    <DropdownMenu>
      <DropdownMenuTrigger>
        {#snippet render(props, _state, content)}
          <Button variant="outline" size="icon" aria-label="More Options" {...props}>{@render content?.()}</Button>
        {/snippet}
        <MoreHorizontalIcon />
      </DropdownMenuTrigger>
      <DropdownMenuContent align="end" class="w-40">
        <DropdownMenuGroup>
          <DropdownMenuItem>
            <MailCheckIcon />
            Mark as Read
          </DropdownMenuItem>
          <DropdownMenuItem>
            <ArchiveIcon />
            Archive
          </DropdownMenuItem>
        </DropdownMenuGroup>
        <DropdownMenuSeparator />
        <DropdownMenuGroup>
          <DropdownMenuItem>
            <ClockIcon />
            Snooze
          </DropdownMenuItem>
          <DropdownMenuItem>
            <CalendarPlusIcon />
            Add to Calendar
          </DropdownMenuItem>
          <DropdownMenuItem>
            <ListFilterIcon />
            Add to List
          </DropdownMenuItem>
          <DropdownMenuSub>
            <DropdownMenuSubTrigger>
              <TagIcon />
              Label As...
            </DropdownMenuSubTrigger>
            <DropdownMenuSubContent>
              <DropdownMenuRadioGroup bind:value={label}>
                <DropdownMenuRadioItem value="personal">Personal</DropdownMenuRadioItem>
                <DropdownMenuRadioItem value="work">Work</DropdownMenuRadioItem>
                <DropdownMenuRadioItem value="other">Other</DropdownMenuRadioItem>
              </DropdownMenuRadioGroup>
            </DropdownMenuSubContent>
          </DropdownMenuSub>
        </DropdownMenuGroup>
        <DropdownMenuSeparator />
        <DropdownMenuGroup>
          <DropdownMenuItem variant="destructive">
            <Trash2Icon />
            Trash
          </DropdownMenuItem>
        </DropdownMenuGroup>
      </DropdownMenuContent>
    </DropdownMenu>
  </ButtonGroup>
</ButtonGroup>
```

## As Link

You can use the `buttonVariants` helper to make a link look like a button.

**Do not use `<Button>` with a `render` snippet and `nativeButton={false}` for links.** The Base `Button` component always applies `role="button"`, which overrides the semantic link role on `<a>` elements. Use `buttonVariants` with a plain `<a>` tag instead.

```svelte
<script lang="ts">
  import { buttonVariants } from '#lib/components/ui/button';
</script>

<a href="#top" class={buttonVariants({ variant: 'secondary', size: 'sm' })}>Login</a>
```

## RTL

To enable RTL support in Sveltery UI, see the [RTL configuration guide](/docs/rtl).

Wrap the buttons in a [DirectionProvider](/docs/components/direction) and set `dir` on their container. Use the language selector to switch the language.

```svelte
<script lang="ts">
  import ArrowRightIcon from '@lucide/svelte/icons/arrow-right';
  import PlusIcon from '@lucide/svelte/icons/plus';
  import { Button } from '#lib/components/ui/button';
  import { Spinner } from '#lib/components/ui/spinner';
  import { useLanguage } from '$lib/components/docs/language.svelte';

  const translations = {
    en: {
      dir: 'ltr',
      values: {
        button: 'Button',
        submit: 'Submit',
        delete: 'Delete',
        loading: 'Loading'
      }
    },
    ar: {
      dir: 'rtl',
      values: {
        button: 'زر',
        submit: 'إرسال',
        delete: 'حذف',
        loading: 'جاري التحميل'
      }
    },
    he: {
      dir: 'rtl',
      values: {
        button: 'כפתור',
        submit: 'שלח',
        delete: 'מחק',
        loading: 'טוען'
      }
    }
  } as const;

  const language = useLanguage();
  const dir = $derived(translations[language.current].dir);
  const t = $derived(translations[language.current].values);
</script>

<div class="flex w-full flex-col gap-4">
  <div class="flex flex-wrap items-center gap-2 md:flex-row" {dir}>
    <Button variant="outline">{t.button}</Button>
    <Button variant="destructive">{t.delete}</Button>
    <Button variant="outline">
      {t.submit}
      <ArrowRightIcon class="rtl:rotate-180" data-icon="inline-end" />
    </Button>
    <Button variant="outline" size="icon" aria-label="Add">
      <PlusIcon />
    </Button>
    <Button variant="secondary" disabled>
      <Spinner data-icon="inline-start" />
      {t.loading}
    </Button>
  </div>
</div>
```

## API Reference

### Button

The `Button` component is a wrapper around the `button` element that adds a variety of styles and functionality. It is styled on top of the Sveltery Base `Button` primitive, which owns its behavior, focus handling and accessibility. See the [Base UI Button documentation](https://base-ui.com/react/components/button#api-reference) for the behavior it ports.

| Prop      | Type                                                                                 | Default     |
| --------- | ------------------------------------------------------------------------------------ | ----------- |
| `variant` | `"default" \| "outline" \| "ghost" \| "destructive" \| "secondary" \| "link"`        | `"default"` |
| `size`    | `"default" \| "xs" \| "sm" \| "lg" \| "icon" \| "icon-xs" \| "icon-sm" \| "icon-lg"` | `"default"` |
