The Button component renders as a native <button> by default (or an anchor, router-link, etc. via the as prop). It provides four distinct interaction states for controlling click behavior and visual feedback.
The loading state can delay its visual indicator through the grace prop — a millisecond window that prevents flicker for fast operations: if the async work completes within it, the loading indicator never appears. The default is 0, so the indicator shows immediately unless you opt in.
Use Button.Loading and Button.Content to swap between loading and default content:
Click the button — the loading indicator appears after a 1-second grace period.
Button.Loading and Button.Content conditionally render based on the loading state. Only one is visible at a time — Content by default, Loading once any configured grace window elapses (immediately with the default grace: 0).
Use Button.Icon to wrap icon content. It sets aria-hidden="true" on itself and detects icon-only buttons — warning in dev when aria-label is missing on Root.
Use Button.HiddenInput inside a group to submit toggle state with forms. It renders a visually hidden checkbox that reflects the button’s selected state.
vue
<script setup lang="ts"> import { Button, Form } from '@vuetify/v0' import { shallowRef } from 'vue' const answer =shallowRef<string>() function onSubmit () { console.log('Answer:', answer.value) }</script><template> <Form @submit="onSubmit"> <Button.Group v-model="answer"> <Button.Root value="yes"> Yes <Button.HiddenInput name="answer" value="yes" /> </Button.Root> <Button.Root value="no"> No <Button.HiddenInput name="answer" value="no" /> </Button.Root> </Button.Group> <button type="submit">Submit</button> </Form></template>
Each state sets a corresponding data-* attribute on the element for CSS styling:
Attribute
When set
data-disabled
disabled prop is true
data-readonly
readonly prop is true
data-passive
passive prop is true
data-loading
Loading grace period has elapsed
data-selected
Button is selected in a group
Tip
disabled uses native disabled attribute and removes the button from tab order. passive uses aria-disabled="true" instead — the button stays focusable and screen readers announce it as disabled.
Both block clicks, but disabled applies the native disabled attribute and drops the button from tab order, while passive uses aria-disabled="true" so the button stays focusable and screen readers announce it as disabled. Reach for passive when a control is only temporarily unavailable.
Set the grace prop (milliseconds, default 0) to delay the indicator. If the async work finishes within that window the indicator never shows, preventing flicker on fast operations.
Put a Button.HiddenInput inside each Button.Root. It renders a visually hidden checkbox that reflects the button’s selected state, so the group’s value posts with the form.
Discord
Need help? Join our community for support and discussions ↗