ComponentsUpdated Sep 9, 2026
Button
The action primitive. Variants carry intent, sizes carry density, and the same styling powers triggers in Dialog, Menu, and Sheet, so a button looks the same wherever it appears.
Import
Source: packages/ui/src/components/button
import { Button, ButtonLink, buttonVariants } from '@highlight/ui'Variants
default for most actions, primary and bold for the one action that matters, secondary for outlined, tertiary for quiet. success, danger, and meeting are for status-bearing actions only.
import { Button } from '@highlight/ui'
export default function ButtonVariants() {
return (
<div className="flex flex-wrap items-center justify-center gap-3">
<Button variant="default">Default</Button>
<Button variant="primary">Primary</Button>
<Button variant="bold">Bold</Button>
<Button variant="secondary">Secondary</Button>
<Button variant="tertiary">Tertiary</Button>
<Button variant="success">Success</Button>
<Button variant="danger">Danger</Button>
<Button variant="meeting">Meeting</Button>
</div>
)
}Sizes
medium is the default. Icons scale with the size automatically.
import { Button } from '@highlight/ui'
export default function ButtonSizes() {
return (
<div className="flex flex-wrap items-center justify-center gap-3">
<Button size="tiny">Tiny</Button>
<Button size="small">Small</Button>
<Button size="medium">Medium</Button>
<Button size="large">Large</Button>
</div>
)
}Shapes
square and circle make icon-only buttons; always add an aria-label. rounded keeps the label with pill corners.
import { ArrowUp } from 'lucide-react'
import { Button } from '@highlight/ui'
export default function ButtonShapes() {
return (
<div className="flex flex-wrap items-center justify-center gap-3">
<Button shape="rounded">Rounded</Button>
<Button aria-label="Send" shape="square">
<ArrowUp />
</Button>
<Button aria-label="Send" shape="circle">
<ArrowUp />
</Button>
<Button aria-label="Send" shape="circle" size="small" variant="primary">
<ArrowUp />
</Button>
</div>
)
}Left and right slots
left and right accept any node; lucide icons are sized for you.
import { ArrowLeft, ArrowRight } from 'lucide-react'
import { Button } from '@highlight/ui'
export default function ButtonIcons() {
return (
<div className="flex flex-wrap items-center justify-center gap-3">
<Button left={<ArrowLeft />}>Back</Button>
<Button right={<ArrowRight />}>Next</Button>
<Button variant="primary" left={<ArrowLeft />} right={<ArrowRight />}>
Both
</Button>
</div>
)
}Hotkey chips
hotkey renders keycap chips from a "cmd+k"-style string. Aliases like cmd, shift, enter, esc map to symbols.
import { Button } from '@highlight/ui'
export default function ButtonHotkey() {
return (
<div className="flex flex-wrap items-center justify-center gap-3">
<Button hotkey="cmd+enter">Send</Button>
<Button variant="primary" hotkey="cmd+k">
Search
</Button>
<Button variant="secondary" size="small" hotkey="esc">
Dismiss
</Button>
</div>
)
}Loading and disabled
loading swaps in a spinner and blocks clicks but keeps full opacity, because the press already landed. disabled fades.
import { useState } from 'react'
import { ArrowUp } from 'lucide-react'
import { Button } from '@highlight/ui'
export default function ButtonStates() {
const [loading, setLoading] = useState(false)
const submit = () => {
setLoading(true)
setTimeout(() => setLoading(false), 1500)
}
return (
<div className="flex flex-wrap items-center justify-center gap-3">
<Button variant="primary" loading={loading} onClick={submit}>
{loading ? 'Saving' : 'Save'}
</Button>
<Button loading>Loading</Button>
<Button loading aria-label="Send" shape="circle">
<ArrowUp />
</Button>
<Button disabled>Disabled</Button>
<Button disabled variant="primary">
Disabled
</Button>
</div>
)
}Links
ButtonLink is an anchor with button styling. target="_blank" adds an external-link icon.
import { ArrowLeft, ArrowUp } from 'lucide-react'
import { ButtonLink } from '@highlight/ui'
export default function ButtonLinks() {
return (
<div className="flex flex-wrap items-center justify-center gap-3">
<ButtonLink href="https://highlightai.com" target="_blank">
External link
</ButtonLink>
<ButtonLink href="#" variant="primary">
Sign up
</ButtonLink>
<ButtonLink href="#" variant="secondary" left={<ArrowLeft />}>
Back
</ButtonLink>
<ButtonLink aria-label="Top" href="#" shape="square">
<ArrowUp />
</ButtonLink>
</div>
)
}Props
Props added on top of the underlying element or Base UI part.
| Prop | Type | Default | Description |
|---|---|---|---|
variant | 'default' | 'primary' | 'bold' | 'secondary' | 'tertiary' | 'success' | 'danger' | 'meeting' | 'default' | Intent. Inherits from a surrounding ButtonGroup when omitted. |
size | 'tiny' | 'small' | 'medium' | 'large' | 'medium' | Height, padding, and icon size. |
shape | 'rounded' | 'square' | 'circle' | — | square and circle are icon-only. |
left / right | ReactNode | — | Leading and trailing slots. |
hotkey | string | — | Keycap chips, e.g. "cmd+enter". |
loading | boolean | false | Shows a spinner in place of the leading slot (or the content for icon buttons) and disables the button. |
- Renders a native <button>; all button attributes pass through, and a ref reaches the element.
- buttonVariants is the cva used for the styling, exported so triggers elsewhere can share it.