Skip to content

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

ClassDescription
.buttonBase button styles
.button--smSmall size variant
.button--mdMedium size variant
.button--lgLarge size variant
.button--primaryPrimary variant
.button--secondarySecondary variant
.button--tertiaryTertiary variant
.button--outlineOutline variant
.button--ghostGhost variant
.button--dangerDanger variant
.button--danger-softSoft danger variant
.button--icon-onlyIcon-only modifier
.button--full-widthFull-width modifier

Interactive States

The Vue button emits the same state attributes expected by the React CSS source:

StateSelector
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

PropTypeDefaultDescription
variant'primary' | 'secondary' | 'tertiary' | 'danger' | 'danger-soft' | 'outline' | 'ghost'undefinedVisual style variant
size'sm' | 'md' | 'lg''md'Button size
disabledbooleanfalseWhether the button is disabled
isDisabledbooleanfalseReact-style disabled alias
isPendingbooleanfalseEmits pending state and prevents pointer interaction
fullWidthbooleanfalseWhether the button takes full width
isIconOnlybooleanfalseWhether the button is icon-only

Events

EventPayloadDescription
clickMouseEventEmitted when button is clicked

Slots

SlotDescription
defaultButton content

Released under the Apache-2.0 License.