Primary action control for forms, dialogs, and navigation.
Introduction
Use buttons for discrete actions: submitting forms, opening dialogs, confirming choices, or navigating when rendered as a link (as="a"). Limit each view to one primary action when possible; use variant="outline" or variant="flat" for secondary actions.
Four visual variants (solid, flat, light, outline) cover filled, subtle, and bordered styles. Use loading for async feedback, density="mini" with icon for icon-only controls, and layout utilities to group related buttons.
Prefer Button for discrete user actions. Use Link for inline textual navigation, and keep one primary solid action per view when possible.
Import
import { Button } from "@bridge-ui/vue/Components/Button";import { Button } from "@bridge-ui/react/Components/Button";Basic usage
The default button is a solid primary action. Use as="a" with href for link-style buttons.
<script setup lang="ts">
import { Button } from "@bridge-ui/vue/Components/Button";
import { Plus } from "@lucide/vue";
</script>
<template>
<div class="flex flex-wrap items-center gap-3">
<Button color="primary">Click me</Button>
<Button variant="outline" :start-icon="Plus">With icon</Button>
<Button as="a" href="https://example.com">External link</Button>
</div>
</template>
import { Button } from "@bridge-ui/react/Components/Button";
import { Plus } from "lucide-react";
export default function ButtonBasic() {
return (
<div className="flex flex-wrap items-center gap-3">
<Button color="primary">Click me</Button>
<Button startIcon={Plus} variant="outline">
With icon
</Button>
<Button as="a" href="https://example.com">
External link
</Button>
</div>
);
}
Colors
Use the color prop to match the action’s intent. Available colors: primary, secondary, success, info, warning, error, and dark.
<script setup lang="ts">
import { Button } from "@bridge-ui/vue/Components/Button";
</script>
<template>
<div class="flex flex-wrap items-center gap-3">
<Button color="primary">primary</Button>
<Button color="secondary">secondary</Button>
<Button color="success">success</Button>
<Button color="info">info</Button>
<Button color="warning">warning</Button>
<Button color="error">error</Button>
<Button color="dark">dark</Button>
</div>
</template>
import { Button } from "@bridge-ui/react/Components/Button";
export default function ButtonColors() {
return (
<div className="flex flex-wrap items-center gap-3">
<Button color="primary">primary</Button>
<Button color="secondary">secondary</Button>
<Button color="success">success</Button>
<Button color="info">info</Button>
<Button color="warning">warning</Button>
<Button color="error">error</Button>
<Button color="dark">dark</Button>
</div>
);
}
Variants
The Button component supports four style variants, set with the variant prop:
- solid (default) — filled background
- flat — subtle background tint
- light — lighter fill
- outline — transparent background with a colored border
<script setup lang="ts">
import { Button } from "@bridge-ui/vue/Components/Button";
</script>
<template>
<div class="flex flex-wrap items-center gap-3">
<Button variant="flat" color="primary">flat</Button>
<Button color="primary" variant="light">light</Button>
<Button color="primary" variant="solid">solid</Button>
<Button color="primary" variant="outline">outline</Button>
</div>
</template>
import { Button } from "@bridge-ui/react/Components/Button";
export default function ButtonVariants() {
return (
<div className="flex flex-wrap items-center gap-3">
<Button variant="flat" color="primary">
flat
</Button>
<Button color="primary" variant="light">
light
</Button>
<Button color="primary" variant="solid">
solid
</Button>
<Button color="primary" variant="outline">
outline
</Button>
</div>
);
}
Sizes
Control the button dimensions with the size prop. Available sizes: 2xs, xs, sm, md (default), lg, xl, and 2xl.
<script setup lang="ts">
import { Button } from "@bridge-ui/vue/Components/Button";
</script>
<template>
<div class="flex flex-wrap items-end gap-3">
<Button size="2xs">2xs</Button>
<Button size="xs">xs</Button>
<Button size="sm">sm</Button>
<Button size="md">md</Button>
<Button size="lg">lg</Button>
<Button size="xl">xl</Button>
<Button size="2xl">2xl</Button>
</div>
</template>
import { Button } from "@bridge-ui/react/Components/Button";
export default function ButtonSizes() {
return (
<div className="flex flex-wrap items-end gap-3">
<Button size="2xs">2xs</Button>
<Button size="xs">xs</Button>
<Button size="sm">sm</Button>
<Button size="md">md</Button>
<Button size="lg">lg</Button>
<Button size="xl">xl</Button>
<Button size="2xl">2xl</Button>
</div>
);
}
Icons
Add icons with startIcon and endIcon. For icon-only buttons, use density="mini" with the icon prop and an aria-label.
<script setup lang="ts">
import { Button } from "@bridge-ui/vue/Components/Button";
import { ArrowRight, Plus, Settings } from "@lucide/vue";
</script>
<template>
<div class="flex flex-wrap items-center gap-3">
<Button :start-icon="Plus">Add</Button>
<Button :end-icon="ArrowRight">Next</Button>
<Button density="mini" :icon="Settings" aria-label="Settings" />
</div>
</template>
import { Button } from "@bridge-ui/react/Components/Button";
import { ArrowRight, Plus, Settings } from "lucide-react";
export default function ButtonIcons() {
return (
<div className="flex flex-wrap items-center gap-3">
<Button startIcon={Plus}>Add</Button>
<Button endIcon={ArrowRight}>Next</Button>
<Button density="mini" icon={Settings} aria-label="Settings" />
</div>
);
}
States
Use loading to show a spinner and disable interaction, or disabled to prevent clicks.
<script setup lang="ts">
import { Button } from "@bridge-ui/vue/Components/Button";
</script>
<template>
<div class="flex flex-wrap items-center gap-3">
<Button loading>Loading</Button>
<Button disabled>Disabled</Button>
</div>
</template>
import { Button } from "@bridge-ui/react/Components/Button";
export default function ButtonStates() {
return (
<div className="flex flex-wrap items-center gap-3">
<Button loading>Loading</Button>
<Button disabled>Disabled</Button>
</div>
);
}
Density and link
Use density="mini" with the icon prop for icon-only buttons. Set full for full-width buttons, or as="a" with href for link-style buttons.
<script setup lang="ts">
import { Button } from "@bridge-ui/vue/Components/Button";
import { Settings } from "@lucide/vue";
</script>
<template>
<div class="flex flex-col gap-4">
<div class="flex flex-wrap items-center gap-3">
<Button>Default density</Button>
<Button density="mini" :icon="Settings" aria-label="Mini density" />
</div>
<div class="flex flex-wrap gap-2">
<Button
density="mini"
variant="solid"
:icon="Settings"
aria-label="Solid"
/>
<Button
density="mini"
:icon="Settings"
variant="outline"
aria-label="Outline"
/>
<Button
density="mini"
variant="flat"
:icon="Settings"
aria-label="Flat"
/>
<Button
density="mini"
variant="light"
:icon="Settings"
aria-label="Light"
/>
</div>
<Button full>Full width button</Button>
<Button as="a" href="https://example.com">External link</Button>
</div>
</template>
import { Button } from "@bridge-ui/react/Components/Button";
import { Settings } from "lucide-react";
const ButtonDensity = () => (
<div className="flex flex-col gap-4">
<div className="flex flex-wrap items-center gap-3">
<Button>Default density</Button>
<Button density="mini" icon={Settings} aria-label="Mini density" />
</div>
<div className="flex flex-wrap gap-2">
<Button
density="mini"
variant="solid"
icon={Settings}
aria-label="Solid"
/>
<Button
density="mini"
icon={Settings}
variant="outline"
aria-label="Outline"
/>
<Button density="mini" variant="flat" icon={Settings} aria-label="Flat" />
<Button
density="mini"
variant="light"
icon={Settings}
aria-label="Light"
/>
</div>
<Button full>Full width button</Button>
<Button as="a" href="https://example.com">
External link
</Button>
</div>
);
export default ButtonDensity;
Rounded
Use the rounded prop to control border radius. Available values: none, xs, sm, md, lg, xl, 2xl, 3xl, 4xl, and full (defaults vary by component).
<script setup lang="ts">
import { Button } from "@bridge-ui/vue/Components/Button";
</script>
<template>
<div class="flex flex-wrap items-center gap-3">
<Button rounded="none">none</Button>
<Button rounded="sm">sm</Button>
<Button rounded="md">md</Button>
<Button rounded="2xl">2xl</Button>
<Button rounded="full">full</Button>
</div>
</template>
import { Button } from "@bridge-ui/react/Components/Button";
export default function ButtonRounded() {
return (
<div className="flex flex-wrap items-center gap-3">
<Button rounded="none">none</Button>
<Button rounded="sm">sm</Button>
<Button rounded="md">md</Button>
<Button rounded="2xl">2xl</Button>
<Button rounded="full">full</Button>
</div>
);
}
Customization
Fine-tune appearance with rounded, className (React) or class (Vue), and the classes prop to target the root element.
<script setup lang="ts">
import { Button } from "@bridge-ui/vue/Components/Button";
</script>
<template>
<Button
rounded="full"
color="primary"
:classes="{ root: 'tracking-widest uppercase shadow-lg' }"
>
Custom
</Button>
</template>
import { Button } from "@bridge-ui/react/Components/Button";
export default function ButtonCustomization() {
return (
<Button
rounded="full"
color="primary"
classes={{
root: "tracking-widest uppercase shadow-lg",
}}
>
Custom
</Button>
);
}
Related components
Accessibility
- Activates with Enter or Space when rendered as button (default)
- Tab moves focus; a visible focus ring is applied via focus:ring-2
- disabled sets the native disabled attribute on <button>; loading also blocks interaction and sets aria-busy
- For as="a" or as="span", the disabled state uses aria-disabled instead of native disabled
- Icon-only buttons (density="mini") need an accessible name via aria-label or visible text
- Link buttons should use meaningful link text
Anatomy
<!-- Default button -->
<button type="button">
<!-- startIcon (optional) -->
Label text
<!-- endIcon (optional) -->
</button>
<!-- Loading state -->
<button type="button" disabled aria-busy="true">
<!-- Loader2 spinner -->
</button>
<!-- Icon-only (density="mini") -->
<button type="button" aria-label="Delete">
<!-- icon -->
</button>API
| Prop | Type | Default | Description |
|---|---|---|---|
| as | "a" | "span" | "button" | "button" | The element to render as. |
| children | ReactNode | — | Button label. |
| classes | ButtonClasses | — | Classes for internal parts. |
| color | ButtonColor | "primary" | Semantic color of the button. |
| customProps | ButtonCustomProps | — | Extra props for internal parts. Root HTML attributes stay on the component top level. |
| density | ButtonDensity | "default" | Density preset. |
| disabled | boolean | false | Whether the button is disabled. |
| endIcon | IconSource | — | Icon at the inline end. |
| full | boolean | false | Whether the button is full width. |
| href | string | — | Link URL when as="a". |
| icon | IconSource | — | Icon for mini density (replaces label and start/end icons). |
| loading | boolean | false | Whether the button is loading. |
| rounded | ButtonRounded | "md" | Border radius. |
| size | ButtonSize | "md" | Button dimensions. |
| slots | ButtonSlots | — | React slots: start, end. |
| startIcon | IconSource | — | Icon at the inline start. |
| text | string | — | Label text when children is not provided. |
| variant | ButtonVariant | "solid" | Visual style variant. |