Button
Overview
A button component that can be used to trigger an action.
Usage
Features
- 🎨 8 variants: solid, outline, dashed, soft, ghost, link, plain, pure
- 🌈 8 colors: primary, destructive, success, warning, info, carbon, secondary, accent
- 📏 6 sizes: xs, sm, md, lg, xl, 2xl
- 🔲 4 shapes: auto, rounded, square, circle
- ⚡ Loading state support
- 🌐 Link function support (SButtonLink)
- ♿ Full accessibility support
- 🎯 TypeScript type safety
Button component family
- SButton - Basic button component
- SButtonLink - Link button, supports route navigation
- SButtonIcon - Icon button, compact design
- SButtonLoading - Loading state button
- SButtonGroup - Button group component
Demos
Basic
Slot
Loading
Icon
Group
API
Aria Layer
5Button
Props
| Prop | Type | Default | Description |
|---|---|---|---|
class | ClassValue | — | Additional class names applied to the root element. |
color | ThemeColor | — | Theme color of the component. |
size | ThemeSize | — | Visual size of the component. |
variant | ButtonVariant | — | Visual variant of the component. |
shape | ButtonShape | — | Shape of the component. |
shadow | ButtonShadow | — | Shadow style of the component. |
fitContent | boolean | — | Whether the component should fit its content width. |
type | ButtonType | 'button' | The type of the button element. Can be one of 'button', 'submit', or 'reset'. |
disabled | boolean | — | Whether the component is disabled. |
asChild | boolean | — | Change the default rendered element for the one passed as a child, merging their props and behavior. |
as | AsTag | Component | 'div' | The element or component this component should render as. Can be overwrite by `asChild` |
Emits
| Emit Name | Parameters | Description |
|---|---|---|
click | PointerEvent | Emitted when click occurs. |
ButtonGroup
Props
| Prop | Type | Default | Description |
|---|---|---|---|
class | ClassValue | — | Additional class names applied to the root element. |
color | ThemeColor | — | Theme color of the component. |
size | ThemeSize | — | Visual size of the component. |
variant | ButtonVariant | — | Visual variant of the component. |
shape | ButtonShape | — | Shape of the component. |
shadow | ButtonShadow | — | Shadow style of the component. |
fitContent | boolean | — | Whether the component should fit its content width. |
disabled | boolean | — | Whether all descendant buttons are disabled. |
orientation | DataOrientation | 'horizontal' | The orientation of the button group. |
dir | Direction | — | The reading direction of the button group. |
asChild | boolean | — | Change the default rendered element for the one passed as a child, merging their props and behavior. |
as | AsTag | Component | 'div' | The element or component this component should render as. Can be overwrite by `asChild` |
ButtonIcon
Props
| Prop | Type | Default | Description |
|---|---|---|---|
icon | IconValue | — | The icon value of iconify. |
iconClass | ClassValue | — | The class of the icon. |
iconProps | Partial<IconProps> | — | The props of the icon. |
class | ClassValue | — | Additional class names applied to the root element. |
color | ThemeColor | — | Theme color of the component. |
size | ThemeSize | — | Visual size of the component. |
variant | ButtonVariant | — | Visual variant of the component. |
shape | ButtonShape | — | Shape of the component. |
shadow | ButtonShadow | — | Shadow style of the component. |
fitContent | boolean | — | Whether the component should fit its content width. |
type | ButtonType | 'button' | The type of the button element. Can be one of 'button', 'submit', or 'reset'. |
disabled | boolean | — | Whether the component is disabled. |
asChild | boolean | — | Change the default rendered element for the one passed as a child, merging their props and behavior. |
as | AsTag | Component | 'div' | The element or component this component should render as. Can be overwrite by `asChild` |
ButtonLink
Props
| Prop | Type | Default | Description |
|---|---|---|---|
class | ClassValue | — | Additional class names applied to the root element. |
color | ThemeColor | — | Theme color of the component. |
size | ThemeSize | — | Visual size of the component. |
variant | ButtonVariant | — | Visual variant of the component. |
shape | ButtonShape | — | Shape of the component. |
shadow | ButtonShadow | — | Shadow style of the component. |
fitContent | boolean | — | Whether the component should fit its content width. |
type | ButtonType | 'button' | The type of the button element. Can be one of 'button', 'submit', or 'reset'. |
disabled | boolean | — | Whether the component is disabled.
When `true`, the link is disabled. |
asChild | boolean | — | Change the default rendered element for the one passed as a child, merging their props and behavior. |
as | AsTag | Component | 'div' | The element or component this component should render as. Can be overwrite by `asChild` |
to | string | import("vue-router").RouteLocationAsRelativeGeneric | import("vue-router").RouteLocationAsPathGeneric | — | Route Location the link should navigate to when clicked on. |
replace | boolean | — | Calls `router.replace` instead of `router.push`. |
href | string | — | The URL the link should navigate to when clicked on. |
activeClass | string | — | Class to apply when the link is active |
exactActiveClass | string | — | Class to apply when the link is exact active |
inactiveClass | string | — | The class to apply to the link when it is inactive. |
prefetchedClass | string | — | A class to apply to links that have been prefetched. |
external | boolean | — | Forces the link to be considered as external (true) or internal (false). This is helpful to handle edge-cases |
ariaCurrentValue | 'true' | 'false' | 'page' | 'date' | 'time' | 'step' | 'location' | `'page'` | Value passed to the attribute `aria-current` when the link is exact active. |
viewTransition | boolean | — | Pass the returned promise of `router.push()` to `document.startViewTransition()` if supported. |
target | (string & {}) | '_blank' | '_parent' | '_self' | '_top' | null | — | Where to display the linked URL, as the name for a browsing context. |
rel | (string & {}) | 'noopener' | 'noreferrer' | 'nofollow' | 'sponsored' | 'ugc' | null | 'noopener noreferrer' | A rel attribute value to apply on the link. Defaults to "noopener noreferrer" for external links. |
noRel | boolean | — | If set to true, no rel attribute will be added to the link |
prefetch | boolean | — | When enabled will prefetch middleware, layouts and payloads of links in the viewport. |
prefetchOn | 'visibility' | 'interaction' | Partial<{ visibility: boolean; interaction: boolean; }> | — | Allows controlling when to prefetch links. By default, prefetch is triggered only on visibility. |
noPrefetch | boolean | — | Escape hatch to disable `prefetch` attribute. |
trailingSlash | 'append' | 'remove' | — | An option to either add or remove trailing slashes in the `href` for this specific link.
Overrides the global `trailingSlash` option if provided. |
ButtonLoading
Props
| Prop | Type | Default | Description |
|---|---|---|---|
loading | boolean | false | Whether the button is loading. |
loadingText | string | — | The text to display when the button is loading.
It will be displayed when the loading position is center. |
loadingDuration | number | 150 ms | The duration of the loading state in milliseconds. |
autoLoading | boolean | false | Whether to automatically handle loading state during click event. When true, the button will show loading state
during click event execution. When false, you need to manually control the loading state. |
loadingIcon | IconValue | 'svg-spinners:270-ring' | The icon name of iconify to display when the button is loading. |
loadingIconProps | Partial<IconProps> | — | The props of the loading icon. |
loadingPosition | Align | 'start' | The position of the loading icon. |
class | ClassValue | — | Additional class names applied to the root element. |
color | ThemeColor | — | Theme color of the component. |
size | ThemeSize | — | Visual size of the component. |
variant | ButtonVariant | — | Visual variant of the component. |
shape | ButtonShape | — | Shape of the component. |
shadow | ButtonShadow | — | Shadow style of the component. |
fitContent | boolean | — | Whether the component should fit its content width. |
type | ButtonType | 'button' | The type of the button element. Can be one of 'button', 'submit', or 'reset'. |
disabled | boolean | — | Whether the component is disabled. |
asChild | boolean | — | Change the default rendered element for the one passed as a child, merging their props and behavior. |
as | AsTag | Component | 'div' | The element or component this component should render as. Can be overwrite by `asChild` |
Notes
Architecture and benchmark differences
VeanUI splits the button into an Aria layer (@vean/aria/button) that owns state, ARIA, and keyboard behavior, and a styled layer (@vean/ui) that owns variants and UnoCSS classes. This mirrors shadcn/ui’s headless/styled separation and differs from single-package libraries such as Ant Design, Element Plus, MUI, Mantine, and Naive UI.
| Aspect | VeanUI | Ant Design / Element Plus / MUI / Mantine / Naive UI |
|---|---|---|
| Architecture | Aria + styled split | single package |
| Styling | UnoCSS utility classes via cv() recipe | CSS-in-JS / SCSS / CSS vars |
| Customization | class prop, as / asChild polymorphism, slot overrides | className, style, component overrides |
| Loading | dedicated SButtonLoading component | loading prop on the base button |
| Icon button | dedicated SButtonIcon component | icon prop on the base button |
FAQ
Why does the disabled button keep aria-disabled in addition to the native disabled? The native disabled attribute removes the button from the tab order and blocks platform-level clicks. aria-disabled="true" is also set so assistive technology can announce the disabled state when the button is rendered as a non-<button> element through the as prop (e.g. as="a" or as="div"), where the native disabled attribute does not apply.
How do I make a full-width button? Pass class="w-full" (or fitContent for the opposite effect). VeanUI does not add a block prop because UnoCSS utilities cover this without expanding the API surface.
How do I show a loading indicator? Use SButtonLoading. It supports autoLoading (toggles loading during the click handler), a controlled loading prop, loadingPosition (start / center / end), loadingText, and a custom loadingIcon. The base SButton intentionally stays loading-free to keep its API minimal. While loading, the button sets aria-busy="true" and the loading icon is aria-hidden so the state is announced without redundant decoration.
How does SButtonGroup propagate props to children?SButtonGroup provides a context that forwards color, size, variant, shape, shadow, disabled, and fitContent to descendant SButton instances. A child’s own prop takes precedence over the group’s value.
Can I render the button as a link or another element? Yes. Use the as prop to change the root tag (as="a", as="div", …), or asChild to merge props onto a custom child. For routing-aware link buttons, prefer SButtonLink, which composes SButton with the Link component.