Button

Buttons for primary, secondary, destructive, and quiet actions.

Use one button for everything from primary actions to quiet icon controls. Change its emphasis and dimensions with variant and size.

tsx
import { Button } from "@/components/ui/button"export function Example() {  return (    <>      <Button variant="outline">Button</Button>      <Button variant="outline" size="icon" aria-label="Upload">        <ArrowUpIcon />      </Button>    </>  )}

Installation

npx @arctis-sh/@arctis-sh/ui@latest add button

Usage

import { Button, ButtonGroup, buttonVariants } from "@/components/ui/button"
<Button variant="outline">Button</Button>
<Button variant="default | secondary | destructive | outline | ghost | link">  Button</Button>

Size

Use size for text buttons. Dedicated icon-only sizes are covered below.

tsx
import { Button } from "@/components/ui/button"export function Example() {  return (    <>      <Button size="xs">        Extra Small        <ArrowUpRightIcon data-icon="inline-end" />      </Button>      <Button size="sm">        Small        <ArrowUpRightIcon data-icon="inline-end" />      </Button>      <Button>        Default        <ArrowUpRightIcon data-icon="inline-end" />      </Button>      <Button size="lg">        Large        <ArrowUpRightIcon data-icon="inline-end" />      </Button>    </>  )}

Default

A solid treatment for the primary action.

tsx
import { Button } from "@/components/ui/button"export function Example() {  return <Button>Button</Button>}

Outline

A bordered treatment with a transparent fill and accent hover.

tsx
import { Button } from "@/components/ui/button"export function Example() {  return <Button variant="outline">Outline</Button>}

Secondary

A softer fill for supporting actions.

tsx
import { Button } from "@/components/ui/button"export function Example() {  return <Button variant="secondary">Secondary</Button>}

Ghost

No fill until hover. A good fit for toolbars and compact interfaces.

tsx
import { Button } from "@/components/ui/button"export function Example() {  return <Button variant="ghost">Ghost</Button>}

Destructive

Use for deletion and other irreversible actions.

tsx
import { Button } from "@/components/ui/button"export function Example() {  return <Button variant="destructive">Destructive</Button>}

An inline text treatment with an underline on hover.

tsx
import { Button } from "@/components/ui/button"export function Example() {  return <Button variant="link">Link</Button>}

Icon

Use the square sizes for icon-only controls, and always provide an aria-label.

tsx
import { Button } from "@/components/ui/button"export function Example() {  return (    <>      <Button size="icon-xs" aria-label="Update">        <CircleFadingArrowUpIcon />      </Button>      <Button size="icon-sm" aria-label="Update">        <CircleFadingArrowUpIcon />      </Button>      <Button size="icon" aria-label="Update">        <CircleFadingArrowUpIcon />      </Button>      <Button size="icon-lg" aria-label="Update">        <CircleFadingArrowUpIcon />      </Button>    </>  )}

With icon

Place an SVG inside the button. Add data-icon="inline-start" or data-icon="inline-end" to tighten spacing on that side.

tsx
import { Button } from "@/components/ui/button"export function Example() {  return (    <>      <Button variant="outline">        <GitBranchIcon data-icon="inline-start" />        New Branch      </Button>      <Button variant="outline">        <GitForkIcon data-icon="inline-start" />        Fork      </Button>    </>  )}

Rounded

Add !rounded-full for a pill shape. It overrides the default rounded-md.

tsx
import { Button } from "@/components/ui/button"export function Example() {  return (    <>      <Button className="!rounded-full">Get Started</Button>      <Button className="!rounded-full" size="icon" aria-label="Upload">        <ArrowUpIcon />      </Button>    </>  )}

Spinner

Set loading to replace the label with a centered spinner. The button keeps its width and remains disabled while loading.

tsx
import { useState } from "react"import { Button } from "@/components/ui/button"export function Example() {  const [loading, setLoading] = useState(false)  return (    <Button      loading={loading}      onClick={() => {        setLoading(true)        window.setTimeout(() => setLoading(false), 1600)      }}    >      Submit    </Button>  )}

Button group

Wrap related buttons in ButtonGroup to join their edges into one compact control.

tsx
import {  Button,  ButtonGroup,  buttonVariants,} from "@/components/ui/button"import {  DropdownMenu,  DropdownMenuContent,  DropdownMenuItem,  DropdownMenuSeparator,  DropdownMenuTrigger,} from "@/components/ui/dropdown-menu"export function Example() {  return (    <div className="flex flex-wrap items-center gap-2">      <Button variant="outline" size="icon" aria-label="Back">        <ArrowLeftIcon />      </Button>      <ButtonGroup>        <Button variant="outline">Archive</Button>        <Button variant="outline">Report</Button>      </ButtonGroup>      <ButtonGroup>        <Button variant="outline">Snooze</Button>        <DropdownMenu>          <DropdownMenuTrigger            aria-label="More actions"            className={buttonVariants({ variant: "outline", size: "icon" })}          >            <MoreHorizontalIcon />          </DropdownMenuTrigger>          <DropdownMenuContent align="end">            <DropdownMenuItem>              <CheckIcon />              Mark as done            </DropdownMenuItem>            <DropdownMenuItem>              <ArchiveIcon />              Move to archive            </DropdownMenuItem>            <DropdownMenuSeparator />            <DropdownMenuItem>              <ClockIcon />              Remind me later            </DropdownMenuItem>            <DropdownMenuItem>              <PinIcon />              Pin this scene            </DropdownMenuItem>            <DropdownMenuItem>              <ShareIcon />              Copy share link            </DropdownMenuItem>          </DropdownMenuContent>        </DropdownMenu>      </ButtonGroup>    </div>  )}
import { Button, ButtonGroup } from "@/components/ui/button"<ButtonGroup>  <Button variant="outline">Archive</Button>  <Button variant="outline">Report</Button></ButtonGroup>

Use buttonVariants on an anchor when the action navigates. This keeps the correct link semantics while matching the button styles.

tsx
import { buttonVariants } from "@/components/ui/button"export function Example() {  return (    <a href="#login" className={buttonVariants()}>      Login    </a>  )}

API Reference

Button

PropTypeDefault
variant"default" | "secondary" | "destructive" | "outline" | "ghost" | "link""default"
size"default" | "xs" | "sm" | "lg" | "icon" | "icon-xs" | "icon-sm" | "icon-lg""default"
loadingbooleanfalse
classNamestring
<Button variant="outline">Save</Button>

buttonVariants

Returns the same classes as Button for links and other elements.

PropTypeDefault
variant"default" | "secondary" | "destructive" | "outline" | "ghost" | "link""default"
size"default" | "xs" | "sm" | "lg" | "icon" | "icon-xs" | "icon-sm" | "icon-lg""default"
classNamestring
<a className={buttonVariants({ variant: "outline" })} href="/docs">  Docs</a>

ButtonGroup

Also exported from @arctis-sh/ui/button. See Button Group.

PropTypeDefault
orientation"horizontal" | "vertical""horizontal"
classNamestring
<ButtonGroup>  <Button variant="outline">Left</Button>  <Button variant="outline">Right</Button></ButtonGroup>