Button
A button component that can be rendered as another tag or focusable when disabled.
<script setup lang="ts">
import { Button } from 'base-ui-vue'
</script>
<template>
<Button
class="flex items-center justify-center h-10 px-3.5 m-0 outline-0 border border-gray-200 rounded-md bg-gray-50 font-inherit text-base font-medium leading-6 text-gray-900 select-none hover:data-disabled:bg-gray-50 hover:bg-gray-100 active:data-disabled:bg-gray-50 active:bg-gray-200 active:shadow-[inset_0_1px_3px_rgba(0,0,0,0.1)] active:border-t-gray-300 active:data-disabled:shadow-none active:data-disabled:border-t-gray-200 focus-visible:outline-2 focus-visible:outline-blue-800 focus-visible:-outline-offset-1 data-disabled:text-gray-500"
>
Submit
</Button>
</template>Usage guidelines
- Submit buttons: Unlike the native button element,
type="submit"must be specified on Button for it to act as a submit button. - Links: The Button component enforces button semantics (
role="button", keyboard interaction, disabled state). It should not be used for links. See Rendering links as buttons below.
Anatomy
Import the component:
<script setup>
import { Button } from 'base-ui-vue'
</script>
<template>
<Button />
</template>Examples
Rendering as another tag
The button can remain keyboard accessible while being rendered as another tag, such as a <div>, by specifying as="div".
<script setup lang="ts">
import { Button } from 'base-ui-vue'
</script>
<template>
<Button
as="div"
class="flex items-center justify-center h-10 px-3.5 m-0 outline-0 border border-gray-200 rounded-md bg-gray-50 font-inherit text-base font-medium leading-6 text-gray-900 select-none hover:data-disabled:bg-gray-50 hover:bg-gray-100 active:data-disabled:bg-gray-50 active:bg-gray-200 active:shadow-[inset_0_1px_3px_rgba(0,0,0,0.1)] active:border-t-gray-300 active:data-disabled:shadow-none active:data-disabled:border-t-gray-200 focus-visible:outline-2 focus-visible:outline-blue-800 focus-visible:-outline-offset-1 data-disabled:text-gray-500"
>
I am a div but act as a button
</Button>
</template>Rendering links as buttons
The Button component enforces button semantics. :native-button="false" signals that the rendered tag is not a <button>, but it must still be a tag that can receive button semantics (role="button", keyboard interaction handlers). Links (<a>) have their own semantics and should not be rendered as buttons through the as prop.
If a link needs to look like a button visually, style the <a> element directly with CSS rather than using the Button component.
Loading states
For buttons that enter a loading state after being clicked, specify the focusable-when-disabled prop to ensure focus remains on the button when it becomes disabled. This prevents focus from being lost and maintains the tab order.
<script setup lang="ts">
import { Button } from 'base-ui-vue'
import { ref } from 'vue'
const loading = ref(false)
function handleClick() {
loading.value = true
setTimeout(() => {
loading.value = false
}, 4000)
}
</script>
<template>
<Button
class="flex items-center justify-center h-10 px-3.5 m-0 outline-0 border border-gray-200 rounded-md bg-gray-50 font-inherit text-base font-medium leading-6 text-gray-900 select-none hover:data-disabled:bg-gray-50 hover:bg-gray-100 active:data-disabled:bg-gray-50 active:bg-gray-200 active:shadow-[inset_0_1px_3px_rgba(0,0,0,0.1)] active:border-t-gray-300 active:data-disabled:shadow-none active:data-disabled:border-t-gray-200 focus-visible:outline-2 focus-visible:outline-blue-800 focus-visible:-outline-offset-1 data-disabled:text-gray-500"
:disabled="loading"
focusable-when-disabled
@click="handleClick"
>
{{ loading ? 'Submitting' : 'Submit' }}
</Button>
</template>Disabled state
<script setup lang="ts">
import { Button } from 'base-ui-vue'
</script>
<template>
<Button
class="flex items-center justify-center h-10 px-3.5 m-0 outline-0 border border-gray-200 rounded-md bg-gray-50 font-inherit text-base font-medium leading-6 text-gray-900 select-none hover:data-disabled:bg-gray-50 hover:bg-gray-100 active:data-disabled:bg-gray-50 active:bg-gray-200 active:shadow-[inset_0_1px_3px_rgba(0,0,0,0.1)] active:border-t-gray-300 active:data-disabled:shadow-none active:data-disabled:border-t-gray-200 focus-visible:outline-2 focus-visible:outline-blue-800 focus-visible:-outline-offset-1 data-disabled:text-gray-500"
disabled
>
Disabled Button
</Button>
</template>Focusable when disabled
<script setup lang="ts">
import { Button } from 'base-ui-vue'
</script>
<template>
<Button
class="flex items-center justify-center h-10 px-3.5 m-0 outline-0 border border-gray-200 rounded-md bg-gray-50 font-inherit text-base font-medium leading-6 text-gray-900 select-none hover:data-disabled:bg-gray-50 hover:bg-gray-100 active:data-disabled:bg-gray-50 active:bg-gray-200 active:shadow-[inset_0_1px_3px_rgba(0,0,0,0.1)] active:border-t-gray-300 active:data-disabled:shadow-none active:data-disabled:border-t-gray-200 focus-visible:outline-2 focus-visible:outline-blue-800 focus-visible:-outline-offset-1 data-disabled:text-gray-500"
focusable-when-disabled
disabled
>
Focusable disabled Button
</Button>
</template>API reference
| Prop | Type | Default | Description |
|---|---|---|---|
as | string | Component | 'button' | The element or component to use for the root node. |
disabled | boolean | false | Whether the button should ignore user interaction. |
focusable-when-disabled | boolean | false | Whether the button should be focusable when disabled. |
native-button | boolean | undefined | Whether the component renders a native <button> element. If undefined, it is inferred from as. |
class | string | ((state: State) => string) | undefined | CSS class applied to the element, or a function that returns a class based on the component's state. |
style | StyleValue | ((state: State) => StyleValue) | undefined | Style applied to the element, or a function that returns a style object based on the component's state. |
| Attribute | Description |
|---|---|
data-disabled | Present when the button is disabled. |