Skip to main content

Button

Preview
View as Markdown

A native button with a loading state and a disabled state that can keep focus.

Unlike most components the button has no root part. It is a single element with optional parts, so the folder-named file is its root (<ui:button>), see Root or the Folder Name.

<primitives:button>
    <primitives:button.indicator state="idle" />
    <primitives:button.indicator state="loading" />
    <primitives:button.indicator state="disabled" />
</primitives:button>
typo3 ui:add button

Read more about installing Components and Primitives.

A disabled button gets the native disabled attribute. Browsers take it out of the tab order, and a focused button loses focus the moment it is disabled.

With focusableWhenDisabled the button gets aria-disabled="true" instead of the disabled attribute and stays focusable. Clicks and the Enter and Space keys are ignored in the capture phase, before any other listener of the button runs, so it still never submits a form or opens the dialog it triggers. Press Tab to compare the two buttons below.

For a button that starts loading after it was activated, set focusableWhenDisabled so focus stays on it while it is disabled.

Some browser and screen reader combinations do not reliably announce a change of the text inside a focused button. Make the changing text the explicit accessible name instead: wrap the indicators in an element with an id and point aria-labelledby at it.

Each indicator is only visible in its state: idle, loading or disabled. loading wins over disabled, and a state without an indicator renders nothing, so add one for every state the button can be in. Decorative content like a spinner needs aria-hidden="true".

The server renders the state, and on the client you change it with api.setLoading() and api.setDisabled(). Mount the button yourself with autoMount="{false}" and a rootId, as below. ui:button only loads its own auto-mounting entry file for a button with focusableWhenDisabled, because a plain button needs no JavaScript:

For a submit button of a form, pass form.api.isSubmitting as loading, see Forms.

The button renders a plain <button> and does not set data-state or an id, so a trigger can merge its own attributes onto it with asChild: aria-expanded and data-state come from the collapsible, type and the disabled handling from the button.

The button is meant to be a part of larger components of your own. Put it into the template, give it a rootId derived from your component's, and mount it from your component's entry file. The component then drives the button from its own logic, here a backup that runs for a moment.

Backup

Last backup 2 days ago

The following tables cover the available props of the Fluid Primitives.

The button. Renders a <button> element.

NameDescriptionRequiredDefault
typestring
The `type` of the button. It defaults to `button`, so a button inside a form does not submit it. Use `submit` or `reset` to opt in.
No
'button'
disabledboolean
Whether the button is disabled.
No
false
loadingboolean
Whether the button is loading. A loading button is disabled too.
No
false
focusableWhenDisabledboolean
Keeps a disabled button focusable. It then gets `aria-disabled` instead of the `disabled` attribute, and clicks and key presses are ignored.
No
false
attributesarray
Additional attributes that should be rendered on the component where ui:attributes is used.
No
[]
asChildboolean
If true the component uses its child only without the component template. Like Radix UI asChild or Base UI render props.
No
-
rootIdstring
The root ID of the component, used for hydration and identification.
No
-
idsarray
The IDs of of the component parts for composition.
No
[]
autoMountboolean
Whether the client initializes the component on its own. Set it to false to mount it yourself with `mount()`
No
true
classstring
The CSS class(es) to be applied to the component.
No
-

Rendered data attributes

AttributeDescription
data-disabledPresent when disabled
data-loadingPresent while the button is loading.

Shows its content only while the button is in its state. Renders a <span> element.

NameDescriptionRequiredDefault
stateEnum\ButtonIndicatorState
The state of the button this indicator is shown in.
Yes
-
attributesarray
Additional attributes that should be rendered on the component where ui:attributes is used.
No
[]
asChildboolean
If true the component uses its child only without the component template. Like Radix UI asChild or Base UI render props.
No
-
classstring
The CSS class(es) to be applied to the component.
No
-
NameDescriptionDefault
idstring
The unique identifier of the button.
-
idsRecord<string, string> | undefined
The ids of the parts other elements reference: root.
-
disabledboolean | undefined
Whether the button is disabled.
-
loadingboolean | undefined
Whether the button is loading. A loading button is disabled too.
-
focusableWhenDisabledboolean | undefined
Keeps a disabled button focusable. It then gets aria-disabled instead of the disabled attribute, and clicks and key presses are ignored.
-
NameTypeDescription
stateButtonStateThe state of the button, which decides the indicators that are shown.
disabledbooleanWhether the button is disabled, either on its own or because it is loading.
loadingbooleanWhether the button is loading.
setDisabled(value: boolean) => voidDisables or enables the button. A loading button stays disabled.
setLoading(value: boolean) => voidStarts or stops loading. A loading button is disabled too.
addEventListener(...args: Parameters<HTMLButtonElement['addEventListener']>) => voidAdds an event listener to the button element, the same as addEventListener of the element.