Button

A button element that can act as a link or trigger an action.

Usage

Label

Use the default slot to set the label of the Button.

<template>
  <UButton>Button</UButton>
</template>

You can achieve the same result by using the label prop.

<template>
  <UButton label="Button" />
</template>

You can pass any property from the Link component such as to, target, etc.

<template>
  <UButton to="https://github.com/nuxt/ui" target="_blank">Button</UButton>
</template>

Color

Use the color prop to change the color of the Button.

<template>
  <UButton color="neutral">Button</UButton>
</template>

Variant

Use the variant prop to change the variant of the Button.

<template>
  <UButton color="neutral" variant="outline">Button</UButton>
</template>

Size

Use the size prop to change the size of the Button.

<template>
  <UButton size="xl">Button</UButton>
</template>

Icon

Use the icon prop to show an Icon inside the Button.

<template>
  <UButton icon="i-lucide-rocket">Button</UButton>
</template>

Use the leading and trailing props to set the icon position or the leading-icon and trailing-icon props to set a different icon for each position.

<template>
  <UButton trailing-icon="i-lucide-arrow-right">Button</UButton>
</template>

The label as prop or slot is optional so you can use the Button as an icon-only button.

<template>
  <UButton icon="i-lucide-search" />
</template>

Avatar

Use the avatar prop to show an Avatar inside the Button.

<template>
  <UButton
    :avatar="{
      src: 'https://github.com/nuxt.png'
    }"
    color="neutral"
    variant="outline"
  >
    Button
  </UButton>
</template>

The label as prop or slot is optional so you can use the Button as an avatar-only button.

<template>
  <UButton
    :avatar="{
      src: 'https://github.com/nuxt.png'
    }"
    color="neutral"
    variant="outline"
  />
</template>

Loading

Use the loading prop to show a loading icon and disable the Button.

<template>
  <UButton loading>Button</UButton>
</template>

Use the loading-auto prop to show the loading icon automatically while the @click promise is pending.

<script setup lang="ts">
async function onClick() {
  return new Promise<void>(res => setTimeout(res, 1000))
}
</script>

<template>
  <UButton loading-auto @click="onClick">
    Button
  </UButton>
</template>

This also works with the Form component.

<script setup lang="ts">
const state = reactive({ fullName: '' })

async function onSubmit() {
  return new Promise<void>(res => setTimeout(res, 1000))
}

async function validate(data: Partial<typeof state>) {
  if (!data.fullName?.length) return [{ name: 'fullName', message: 'Required' }]
  return []
}
</script>

<template>
  <UForm :state="state" :validate="validate" @submit="onSubmit">
    <UFormField name="fullName" label="Full name">
      <UInput v-model="state.fullName" />
    </UFormField>
    <UButton type="submit" class="mt-2" loading-auto>
      Submit
    </UButton>
  </UForm>
</template>

Loading Icon

Use the loading-icon prop to customize the loading icon. Defaults to i-lucide-refresh-ccw.

<template>
  <UButton loading loading-icon="i-lucide-repeat-2">Button</UButton>
</template>
You can customize this icon globally in your app.config.ts under ui.icons.loading key.

Disabled

Use the disabled prop to disable the Button.

<template>
  <UButton disabled>Button</UButton>
</template>

Examples

class prop

Use the class prop to override the base styles of the Button.

<template>
  <UButton class="font-bold rounded-full">Button</UButton>
</template>

ui prop

Use the ui prop to override the slots styles of the Button.

<template>
  <UButton
    icon="i-lucide-rocket"
    color="neutral"
    variant="outline"
    :ui="{
      leadingIcon: 'text-[var(--ui-primary)]'
    }"
  >
    Button
  </UButton>
</template>

API

Props

Prop Default Type
as

'button'

any

The element or component this component should render as when not a link.

label

string

color

primary

"error" | "primary" | "secondary" | "success" | "info" | "warning" | "neutral"

variant

solid

"link" | "solid" | "outline" | "soft" | "subtle" | "ghost"

size

md

"md" | "xs" | "sm" | "lg" | "xl"

square

boolean

Render the button with equal padding on all sides.

block

boolean

Render the button full width.

loadingAuto

boolean

Set loading state automatically based on the @click promise state

icon

string

Display an icon based on the leading and trailing props.

avatar

AvatarProps

Display an avatar on the left side.

leading

boolean

When true, the icon will be displayed on the left side.

leadingIcon

string

Display an icon on the left side.

trailing

boolean

When true, the icon will be displayed on the right side.

trailingIcon

string

Display an icon on the right side.

loading

boolean

When true, the loading icon will be displayed.

loadingIcon

appConfig.ui.icons.loading

string

The icon when the loading prop is true.

type

'button'

"reset" | "submit" | "button"

The type of the button when not a link.

target

null | "_blank" | "_parent" | "_self" | "_top" | string & {}

Where to display the linked URL, as the name for a browsing context.

disabled

boolean

to

string | RouteLocationAsRelativeGeneric | RouteLocationAsPathGeneric

Route Location the link should navigate to when clicked on.

active

boolean

Force the link to be active independent of the current route.

ui

PartialString<{ base: string[]; label: string; leadingIcon: string; leadingAvatar: string; leadingAvatarSize: string; trailingIcon: string; }>

The Button component extends the Link component. Check out the source code on GitHub.

Slots

Slot Type
leading

{}

default

{}

trailing

{}

Theme

app.config.ts
export default defineAppConfig({
  ui: {
    button: {
      slots: {
        base: [
          'rounded-[calc(var(--ui-radius)*1.5)] font-medium inline-flex items-center focus:outline-none disabled:cursor-not-allowed aria-disabled:cursor-not-allowed disabled:opacity-75 aria-disabled:opacity-75',
          'transition-colors'
        ],
        label: 'truncate',
        leadingIcon: 'shrink-0',
        leadingAvatar: 'shrink-0',
        leadingAvatarSize: '',
        trailingIcon: 'shrink-0'
      },
      variants: {
        buttonGroup: {
          horizontal: 'not-only:first:rounded-e-none not-only:last:rounded-s-none not-last:not-first:rounded-none',
          vertical: 'not-only:first:rounded-b-none not-only:last:rounded-t-none not-last:not-first:rounded-none'
        },
        color: {
          primary: '',
          secondary: '',
          success: '',
          info: '',
          warning: '',
          error: '',
          neutral: ''
        },
        variant: {
          solid: '',
          outline: '',
          soft: '',
          subtle: '',
          ghost: '',
          link: ''
        },
        size: {
          xs: {
            base: 'px-2 py-1 text-xs gap-1',
            leadingIcon: 'size-4',
            leadingAvatarSize: '3xs',
            trailingIcon: 'size-4'
          },
          sm: {
            base: 'px-2.5 py-1.5 text-xs gap-1.5',
            leadingIcon: 'size-4',
            leadingAvatarSize: '3xs',
            trailingIcon: 'size-4'
          },
          md: {
            base: 'px-2.5 py-1.5 text-sm gap-1.5',
            leadingIcon: 'size-5',
            leadingAvatarSize: '2xs',
            trailingIcon: 'size-5'
          },
          lg: {
            base: 'px-3 py-2 text-sm gap-2',
            leadingIcon: 'size-5',
            leadingAvatarSize: '2xs',
            trailingIcon: 'size-5'
          },
          xl: {
            base: 'px-3 py-2 text-base gap-2',
            leadingIcon: 'size-6',
            leadingAvatarSize: 'xs',
            trailingIcon: 'size-6'
          }
        },
        block: {
          true: {
            base: 'w-full justify-center',
            leadingAvatarSize: 'xs',
            trailingIcon: 'ms-auto'
          }
        },
        square: {
          true: ''
        },
        leading: {
          true: ''
        },
        trailing: {
          true: ''
        },
        loading: {
          true: ''
        }
      },
      compoundVariants: [
        {
          color: 'primary',
          variant: 'solid',
          class: 'text-[var(--ui-bg)] bg-[var(--ui-primary)] hover:bg-[var(--ui-primary)]/75 disabled:bg-[var(--ui-primary)] aria-disabled:bg-[var(--ui-primary)] focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-[var(--ui-primary)]'
        },
        {
          color: 'primary',
          variant: 'outline',
          class: 'ring ring-inset ring-[var(--ui-primary)]/50 text-[var(--ui-primary)] hover:bg-[var(--ui-primary)]/10 disabled:bg-transparent aria-disabled:bg-transparent dark:disabled:bg-transparent dark:aria-disabled:bg-transparent focus-visible:ring-2 focus-visible:ring-[var(--ui-primary)]'
        },
        {
          color: 'primary',
          variant: 'soft',
          class: 'text-[var(--ui-primary)] bg-[var(--ui-primary)]/10 hover:bg-[var(--ui-primary)]/15 focus-visible:bg-[var(--ui-primary)]/15 disabled:bg-[var(--ui-primary)]/10 aria-disabled:bg-[var(--ui-primary)]/10'
        },
        {
          color: 'primary',
          variant: 'subtle',
          class: 'text-[var(--ui-primary)] ring ring-inset ring-[var(--ui-primary)]/25 bg-[var(--ui-primary)]/10 hover:bg-[var(--ui-primary)]/15 disabled:bg-[var(--ui-primary)]/10 aria-disabled:bg-[var(--ui-primary)]/10 focus-visible:ring-2 focus-visible:ring-[var(--ui-primary)]'
        },
        {
          color: 'primary',
          variant: 'ghost',
          class: 'text-[var(--ui-primary)] hover:bg-[var(--ui-primary)]/10 focus-visible:bg-[var(--ui-primary)]/10 disabled:bg-transparent aria-disabled:bg-transparent dark:disabled:bg-transparent dark:aria-disabled:bg-transparent'
        },
        {
          color: 'primary',
          variant: 'link',
          class: 'text-[var(--ui-primary)] hover:text-[var(--ui-primary)]/75 disabled:text-[var(--ui-primary)] aria-disabled:text-[var(--ui-primary)] focus-visible:ring-2 focus-visible:ring-inset focus-visible:ring-[var(--ui-primary)]'
        },
        {
          color: 'neutral',
          variant: 'solid',
          class: 'text-[var(--ui-bg)] bg-[var(--ui-bg-inverted)] hover:bg-[var(--ui-bg-inverted)]/90 disabled:bg-[var(--ui-bg-inverted)] aria-disabled:bg-[var(--ui-bg-inverted)] focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-[var(--ui-border-inverted)]'
        },
        {
          color: 'neutral',
          variant: 'outline',
          class: 'ring ring-inset ring-[var(--ui-border-accented)] text-[var(--ui-text)] bg-[var(--ui-bg)] hover:bg-[var(--ui-bg-elevated)] disabled:bg-[var(--ui-bg)] aria-disabled:bg-[var(--ui-bg)] focus-visible:ring-2 focus-visible:ring-[var(--ui-border-inverted)]'
        },
        {
          color: 'neutral',
          variant: 'soft',
          class: 'text-[var(--ui-text)] bg-[var(--ui-bg-elevated)] hover:bg-[var(--ui-bg-accented)]/75 focus-visible:bg-[var(--ui-bg-accented)]/75 disabled:bg-[var(--ui-bg-elevated)] aria-disabled:bg-[var(--ui-bg-elevated)]'
        },
        {
          color: 'neutral',
          variant: 'subtle',
          class: 'ring ring-inset ring-[var(--ui-border-accented)] text-[var(--ui-text)] bg-[var(--ui-bg-elevated)] hover:bg-[var(--ui-bg-accented)]/75 disabled:bg-[var(--ui-bg-elevated)] aria-disabled:bg-[var(--ui-bg-elevated)] focus-visible:ring-2 focus-visible:ring-[var(--ui-border-inverted)]'
        },
        {
          color: 'neutral',
          variant: 'ghost',
          class: 'text-[var(--ui-text)] hover:bg-[var(--ui-bg-elevated)] focus-visible:bg-[var(--ui-bg-elevated)] hover:disabled:bg-transparent dark:hover:disabled:bg-transparent hover:aria-disabled:bg-transparent dark:hover:aria-disabled:bg-transparent'
        },
        {
          color: 'neutral',
          variant: 'link',
          class: 'text-[var(--ui-text-muted)] hover:text-[var(--ui-text)] disabled:text-[var(--ui-text-muted)] aria-disabled:text-[var(--ui-text-muted)] focus-visible:ring-inset focus-visible:ring-2 focus-visible:ring-[var(--ui-border-inverted)]'
        },
        {
          size: 'xs',
          square: true,
          class: 'p-1'
        },
        {
          size: 'sm',
          square: true,
          class: 'p-1.5'
        },
        {
          size: 'md',
          square: true,
          class: 'p-1.5'
        },
        {
          size: 'lg',
          square: true,
          class: 'p-2'
        },
        {
          size: 'xl',
          square: true,
          class: 'p-2'
        },
        {
          loading: true,
          leading: true,
          class: {
            leadingIcon: 'animate-spin'
          }
        },
        {
          loading: true,
          leading: false,
          trailing: true,
          class: {
            trailingIcon: 'animate-spin'
          }
        }
      ],
      defaultVariants: {
        color: 'primary',
        variant: 'solid',
        size: 'md'
      }
    }
  }
})
Some colors in compoundVariants are omitted for readability. Check out the source code on GitHub.