Button
A clickable button component with multiple variants and states.
Import
ts
import { Button } from '@heroui-vue/vue'Usage
Basic
<template>
<Button @click="handlePress">Click me</Button>
</template>
<script setup>
import { Button } from '@heroui-vue/vue'
const handlePress = () => {
console.log('Button pressed')
}
</script>Variants
<template>
<div class="flex flex-wrap gap-3">
<Button>Primary</Button>
<Button variant="secondary">Secondary</Button>
<Button variant="tertiary">Tertiary</Button>
<Button variant="outline">Outline</Button>
<Button variant="ghost">Ghost</Button>
<Button variant="danger">Danger</Button>
<Button variant="danger-soft">Danger Soft</Button>
</div>
</template>
<script setup>
import { Button } from '@heroui-vue/vue'
</script>With Icons
<template>
<div class="flex flex-wrap justify-center gap-3">
<Button>
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
<circle cx="12" cy="12" r="10" />
<path d="M2 12h20" />
<path d="M12 2a15.3 15.3 0 0 1 0 20" />
<path d="M12 2a15.3 15.3 0 0 0 0 20" />
</svg>
Search
</Button>
<Button variant="secondary">
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
<path d="M12 5v14" />
<path d="M5 12h14" />
</svg>
Add Member
</Button>
<Button variant="tertiary">
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
<rect x="3" y="5" width="18" height="14" rx="2" />
<path d="m3 7 9 6 9-6" />
</svg>
Email
</Button>
<Button variant="danger">
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
<path d="M3 6h18" />
<path d="M8 6V4h8v2" />
<path d="m19 6-1 14H6L5 6" />
</svg>
Delete
</Button>
</div>
</template>
<script setup>
import { Button } from '@heroui-vue/vue'
</script>Icon Only
<template>
<div class="flex justify-center gap-3">
<Button aria-label="More actions" is-icon-only variant="tertiary">
<svg viewBox="0 0 24 24" fill="currentColor">
<circle cx="5" cy="12" r="2" />
<circle cx="12" cy="12" r="2" />
<circle cx="19" cy="12" r="2" />
</svg>
</Button>
<Button aria-label="Settings" is-icon-only variant="secondary">
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
<path d="M12 15.5A3.5 3.5 0 1 0 12 8a3.5 3.5 0 0 0 0 7.5Z" />
<path d="M19.4 15a1.8 1.8 0 0 0 .4 2l.1.1a2 2 0 1 1-2.8 2.8l-.1-.1a1.8 1.8 0 0 0-2-.4 1.8 1.8 0 0 0-1 1.6V21a2 2 0 1 1-4 0v-.1a1.8 1.8 0 0 0-1-1.6 1.8 1.8 0 0 0-2 .4l-.1.1a2 2 0 1 1-2.8-2.8l.1-.1a1.8 1.8 0 0 0 .4-2 1.8 1.8 0 0 0-1.6-1H3a2 2 0 1 1 0-4h.1a1.8 1.8 0 0 0 1.6-1 1.8 1.8 0 0 0-.4-2l-.1-.1a2 2 0 1 1 2.8-2.8l.1.1a1.8 1.8 0 0 0 2 .4 1.8 1.8 0 0 0 1-1.6V3a2 2 0 1 1 4 0v.1a1.8 1.8 0 0 0 1 1.6 1.8 1.8 0 0 0 2-.4l.1-.1a2 2 0 1 1 2.8 2.8l-.1.1a1.8 1.8 0 0 0-.4 2 1.8 1.8 0 0 0 1.6 1H21a2 2 0 1 1 0 4h-.1a1.8 1.8 0 0 0-1.5 1Z" />
</svg>
</Button>
<Button aria-label="Delete" is-icon-only variant="danger">
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
<path d="M3 6h18" />
<path d="M8 6V4h8v2" />
<path d="m19 6-1 14H6L5 6" />
</svg>
</Button>
</div>
</template>
<script setup>
import { Button } from '@heroui-vue/vue'
</script>Loading
<template>
<Button is-pending>
<Spinner color="current" size="sm" />
Uploading...
</Button>
</template>
<script setup>
import { Button, Spinner } from '@heroui-vue/vue'
</script>Loading State
<template>
<Button :is-pending="isLoading" @click="handlePress">
<Spinner v-if="isLoading" color="current" size="sm" />
<svg v-else viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
<path d="m21.4 11.6-9.5 9.5a6 6 0 0 1-8.5-8.5l9.5-9.5a4 4 0 0 1 5.7 5.7l-9.6 9.5a2 2 0 0 1-2.8-2.8l8.8-8.8" />
</svg>
{{ isLoading ? 'Uploading...' : 'Upload File' }}
</Button>
</template>
<script setup>
import { ref } from 'vue'
import { Button, Spinner } from '@heroui-vue/vue'
const isLoading = ref(false)
const handlePress = () => {
isLoading.value = true
window.setTimeout(() => {
isLoading.value = false
}, 2000)
}
</script>Sizes
<template>
<div class="flex items-center gap-3">
<Button size="sm">Small</Button>
<Button size="md">Medium</Button>
<Button size="lg">Large</Button>
</div>
</template>
<script setup>
import { Button } from '@heroui-vue/vue'
</script>Full Width
<template>
<div class="w-full max-w-sm space-y-3">
<Button full-width>Primary Button</Button>
<Button full-width>
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
<path d="M12 5v14" />
<path d="M5 12h14" />
</svg>
With Icon
</Button>
</div>
</template>
<script setup>
import { Button } from '@heroui-vue/vue'
</script>Disabled
<template>
<div class="flex flex-wrap gap-3">
<Button is-disabled>Primary</Button>
<Button is-disabled variant="secondary">Secondary</Button>
<Button is-disabled variant="tertiary">Tertiary</Button>
<Button is-disabled variant="outline">Outline</Button>
<Button is-disabled variant="ghost">Ghost</Button>
<Button is-disabled variant="danger">Danger</Button>
</div>
</template>
<script setup>
import { Button } from '@heroui-vue/vue'
</script>Social Buttons
<template>
<div class="flex w-full max-w-xs flex-col gap-3">
<Button class="w-full" variant="tertiary">
<svg viewBox="0 0 24 24" fill="currentColor">
<path fill="#4285F4" d="M22.6 12.2c0-.8-.1-1.6-.2-2.3H12v4.4h5.9a5 5 0 0 1-2.2 3.3v2.7h3.6c2.1-1.9 3.3-4.8 3.3-8.1Z" />
<path fill="#34A853" d="M12 23c3 0 5.5-1 7.3-2.7l-3.6-2.7c-1 .7-2.2 1.1-3.7 1.1a6.4 6.4 0 0 1-6-4.4H2.3v2.8A11 11 0 0 0 12 23Z" />
<path fill="#FBBC05" d="M6 14.3a6.6 6.6 0 0 1 0-4.2V7.3H2.3a11 11 0 0 0 0 9.8L6 14.3Z" />
<path fill="#EA4335" d="M12 5.5c1.6 0 3.1.6 4.2 1.7l3.1-3.1A10.5 10.5 0 0 0 12 1 11 11 0 0 0 2.3 7.3L6 10.1a6.4 6.4 0 0 1 6-4.6Z" />
</svg>
Sign in with Google
</Button>
<Button class="w-full" variant="tertiary">
<svg viewBox="0 0 24 24" fill="currentColor">
<path d="M12 .5a12 12 0 0 0-3.8 23c.6.1.8-.2.8-.6v-2.1c-3.3.7-4-1.4-4-1.4-.5-1.3-1.3-1.7-1.3-1.7-1-.7.1-.7.1-.7 1.2.1 1.8 1.2 1.8 1.2 1 .1.8 2.7 3.4 1.9.1-.8.4-1.3.7-1.6-2.6-.3-5.4-1.3-5.4-5.9 0-1.3.5-2.4 1.2-3.2-.1-.3-.5-1.6.1-3.2 0 0 1-.3 3.3 1.2a11.3 11.3 0 0 1 6 0c2.3-1.5 3.3-1.2 3.3-1.2.6 1.6.2 2.9.1 3.2.8.8 1.2 1.9 1.2 3.2 0 4.6-2.8 5.6-5.4 5.9.4.4.8 1.1.8 2.2v3.2c0 .4.2.7.8.6A12 12 0 0 0 12 .5Z" />
</svg>
Sign in with GitHub
</Button>
<Button class="w-full" variant="tertiary">
<svg viewBox="0 0 24 24" fill="currentColor">
<path d="M16.4 1.9c0 1.1-.4 2.1-1.2 2.9-.8.9-1.9 1.5-3 1.4-.1-1.1.4-2.2 1.1-3 .8-.8 2-1.4 3.1-1.3ZM20.4 17.5c-.6 1.3-.9 1.9-1.7 3.1-1.1 1.6-2.7 3.5-4.6 3.5-1.7 0-2.1-1.1-4.4-1.1s-2.8 1.1-4.4 1.1c-1.9 0-3.3-1.7-4.4-3.3-3-4.5-3.3-9.8-1.5-12.6 1.3-2 3.3-3.1 5.2-3.1 2 0 3.2 1.1 4.8 1.1 1.6 0 2.6-1.1 4.9-1.1 1.7 0 3.6.9 4.9 2.6-4.3 2.4-3.6 8.4 1.2 9.8Z" />
</svg>
Sign in with Apple
</Button>
</div>
</template>
<script setup>
import { Button } from '@heroui-vue/vue'
</script>Styling
Passing Tailwind CSS classes
vue
<template>
<Button class="bg-purple-500 text-white hover:bg-purple-600">
Purple Button
</Button>
</template>CSS Classes
| Class | Description |
|---|---|
.button | Base button styles |
.button--sm | Small size variant |
.button--md | Medium size variant |
.button--lg | Large size variant |
.button--primary | Primary variant |
.button--secondary | Secondary variant |
.button--tertiary | Tertiary variant |
.button--outline | Outline variant |
.button--ghost | Ghost variant |
.button--danger | Danger variant |
.button--danger-soft | Soft danger variant |
.button--icon-only | Icon-only modifier |
.button--full-width | Full-width modifier |
Interactive States
The Vue button emits the same state attributes expected by the React CSS source:
| State | Selector |
|---|---|
| Hover | [data-hovered="true"] |
| Pressed | [data-pressed="true"] |
| Focus visible | [data-focus-visible="true"] |
| Disabled | [aria-disabled="true"] / [data-disabled="true"] |
| Pending | [data-pending="true"] |
API
Props
| Prop | Type | Default | Description |
|---|---|---|---|
variant | 'primary' | 'secondary' | 'tertiary' | 'danger' | 'danger-soft' | 'outline' | 'ghost' | undefined | Visual style variant |
size | 'sm' | 'md' | 'lg' | 'md' | Button size |
disabled | boolean | false | Whether the button is disabled |
isDisabled | boolean | false | React-style disabled alias |
isPending | boolean | false | Emits pending state and prevents pointer interaction |
fullWidth | boolean | false | Whether the button takes full width |
isIconOnly | boolean | false | Whether the button is icon-only |
Events
| Event | Payload | Description |
|---|---|---|
click | MouseEvent | Emitted when button is clicked |
Slots
| Slot | Description |
|---|---|
default | Button content |