Button
A clickable element that runs an action, submits a form or opens a link.
<Button
variant="subtle"
theme="gray"
size="sm"
label="Save"
/>Examples
List view controls
Filter and view controls above a list. icon-left and icon-right put an icon beside the label, icon alone makes an icon-only button, and the #suffix slot holds a count.
<script setup>
import { Button } from 'frappe-ui'
</script>
<template>
<div class="flex w-full max-w-[1100px] items-center gap-8">
<div class="flex flex-1 items-center gap-1.5">
<Button>Lead owner</Button>
<Button icon-right="lucide-chevron-down">Status</Button>
<Button icon-right="lucide-chevron-down">Organisation</Button>
</div>
<div class="flex items-center gap-2">
<Button icon-left="lucide-eye-off">Columns</Button>
<Button icon-left="lucide-list-filter">
Filter
<template #suffix>
<span
class="inline-flex h-4.5 items-center justify-center rounded-1 bg-surface-base px-1.5 text-xs text-ink-gray-7 shadow-sm ring-1 ring-black/5"
>
1
</span>
</template>
</Button>
<Button icon-left="lucide-arrow-up-down">Sort</Button>
<Button icon="lucide-ellipsis" label="More" />
</div>
</div>
</template>Section header
A ghost button with the section name, and an add action at the other end of the row.
<script setup>
import { Button } from 'frappe-ui'
</script>
<template>
<div class="flex w-full max-w-[352px] items-center gap-2 p-3">
<Button
variant="ghost"
icon-left="lucide-chevron-down"
class="-ml-2 font-medium"
>
Contacts
</Button>
<div class="flex-1" />
<Button icon-left="lucide-plus">Add contact</Button>
</div>
</template>Selection toolbar
A row of ghost buttons that act on the selected items. theme="red" marks the destructive action.
<script setup>
import { Button } from 'frappe-ui'
</script>
<template>
<div
class="inline-flex items-center gap-3.5 rounded-6 bg-surface-base p-1.5 shadow-[0px_0px_1.5px_rgba(0,0,0,0.16),0px_2px_5px_rgba(0,0,0,0.14)]"
>
<Button variant="ghost" icon-left="lucide-x">2 selected</Button>
<div class="h-5 border-l border-outline-gray-1" />
<div class="flex items-center gap-1">
<Button variant="ghost" icon-left="lucide-download">Download</Button>
<Button variant="ghost" icon-left="lucide-corner-up-right">Move</Button>
<Button variant="ghost" icon-left="lucide-share-2">Share</Button>
<Button variant="ghost" theme="red" icon-left="lucide-trash-2">
Delete
</Button>
</div>
</div>
</template>Primary and secondary action
A solid button for the main action beside a default subtle one.
<script setup>
import { Button } from 'frappe-ui'
</script>
<template>
<div class="flex items-center gap-2">
<Button>Join</Button>
<Button variant="solid">Reschedule</Button>
</div>
</template>Full-width actions
Add class="w-full" to stack buttons in a narrow column.
<script setup>
import { Button } from 'frappe-ui'
</script>
<template>
<div class="flex w-58 flex-col items-stretch gap-2">
<Button class="w-full">Join</Button>
<Button variant="solid" class="w-full">Reschedule</Button>
</div>
</template>Card actions
Two actions that share the width of a card, each with class="flex-1".
<script setup>
import { Button } from 'frappe-ui'
</script>
<template>
<div
class="flex w-96 flex-col gap-6 rounded-6 bg-surface-base p-4 shadow-[0px_0px_0.75px_rgba(0,0,0,0.16),0px_2px_2.5px_rgba(0,0,0,0.14)]"
>
<div class="flex flex-col gap-5">
<div class="flex flex-col gap-3">
<p class="text-md-semibold text-ink-gray-7">Frappe january - 2023</p>
<div class="flex flex-col gap-3">
<div class="flex items-center gap-2 text-base text-ink-gray-7">
<span class="lucide-calendar size-4" aria-hidden="true" />
<span>Wed, Dec 27, 2023</span>
</div>
<div class="flex items-center gap-2 text-base text-ink-gray-7">
<span class="lucide-clock size-4" aria-hidden="true" />
<span>03:45 PM - 04:15 PM (30 min)</span>
</div>
</div>
</div>
<p class="text-base leading-normal text-ink-gray-7">
Digital products are more abstract and complex than any product we’v…
</p>
</div>
<div class="flex items-center gap-2">
<Button class="flex-1">Start</Button>
<Button variant="solid" class="flex-1" icon-left="lucide-video">
Join
</Button>
</div>
</div>
</template>Behavior
Pressed state
Button shows its pressed look while its data-state attribute is open or active. There is no active prop.
As a menu trigger, this needs no wiring. The menu sets data-state="open" on the trigger, so the button stays pressed while the menu is open.
<Dropdown :options="options" :button="{ label: 'Options' }" />For a standalone toggle, such as a toolbar control or a filter chip, set the attribute yourself. It changes only the look, so pair it with aria-pressed:
<Button
label="Bold"
:data-state="isBold ? 'active' : 'inactive'"
:aria-pressed="isBold"
/>Loading
loading shows a spinner in place of the left icon and makes the button non-interactive, without the dimmed disabled look. loading-text replaces the label while it loads. Only disabled dims the button.
Links
With route, the button renders as a router link. With href, it renders as an <a> that opens the URL in a new tab. A disabled or loading button always renders as a native <button>, so it cannot be followed. type sets the native button type and defaults to button.
Accessibility
- An icon-only button needs a
label. The label is not shown. It becomes the button'saria-label. - A loading button sets
aria-busy="true".
API Reference
Show types
import type { RouteDestination } from '../shared/route'
import { type Component, type ExtractPublicPropTypes, type PropType } from 'vue'
export type Theme = 'gray' | 'blue' | 'green' | 'red'
export type Size = 'xs' | 'sm' | 'md' | 'lg'
export type Variant = 'solid' | 'subtle' | 'outline' | 'ghost'
const iconProp = {
type: [String, Object, Function] as PropType<string | Component>,
default: undefined,
}
/**
* Runtime prop definitions — the single source of truth for the button's props.
* `Button.vue` spreads these into `defineComponent`, and the public `ButtonProps`
* type is derived from them, so the runtime and the type can never drift apart.
*/
export const buttonProps = {
/** Visual color theme of the button */
theme: { type: String as PropType<Theme>, default: 'gray' },
/** Controls the button size */
size: { type: String as PropType<Size>, default: 'sm' },
/** Visual style of the button */
variant: { type: String as PropType<Variant>, default: 'subtle' },
/** Text label displayed inside the button */
label: { type: String, default: undefined },
/** Icon shown when no left or right icon is specified */
icon: iconProp,
/** Icon shown before the label */
iconLeft: iconProp,
/** Icon shown after the label */
iconRight: iconProp,
/** Tooltip text shown on hover */
tooltip: { type: String, default: undefined },
/** Shows a loading state and disables interaction */
loading: { type: Boolean, default: false },
/** Text shown while the button is loading */
loadingText: { type: String, default: undefined },
/** Disables the button */
disabled: { type: Boolean, default: false },
/** Router destination when used as a link */
route: {
type: [String, Object] as PropType<RouteDestination>,
default: undefined,
},
/** External URL, opened in a new tab. */
href: { type: String, default: undefined },
/** Native button type */
type: {
type: String as PropType<'button' | 'submit' | 'reset'>,
default: 'button',
},
}
/** Public prop types for `<Button>`. Derived from {@link buttonProps}. */
export type ButtonProps = ExtractPublicPropTypes<typeof buttonProps>
/** Combined theme and variant key */
export type ThemeVariant = `${Theme}-${Variant}`Visual color theme of the button
Controls the button size
Visual style of the button
Text label displayed inside the button
Icon shown when no left or right icon is specified
Icon shown before the label
Icon shown after the label
Tooltip text shown on hover
Shows a loading state and disables interaction
Text shown while the button is loading
Disables the button
Router destination when used as a link
External URL, opened in a new tab.
Native button type
| Slot | Payload |
|---|---|
prefix | [void] Content shown before the button label (left icon / custom content) |
icon | [void] Icon-only content for icon buttons |
default | [void] Main button content (overrides `label`) |
suffix | [void] Content shown after the button label (right icon / custom content) |
Content shown before the button label (left icon / custom content)
Icon-only content for icon buttons
Main button content (overrides `label`)
Content shown after the button label (right icon / custom content)