Live demo — this example is server-driven; run the showcase locally for the interactive version.

Button

Interactive controls for actions and navigation. Renders as a <button> by default, an <a> when href is set, or any tag via component. Variants, sizes, count badges, progress states, and specialised treatments (hamburger, favorite, settings, stateful) are all composed on the same wrapper.

Examples

Variant examples

All eight variants: primary, secondary, tertiary, danger, warning, link, plain (icon-only), control.

Disabled buttons

disabled=true renders the HTML disabled attribute — fully non-interactive, removed from the tab order. For an aria-disabled variant that stays focusable, see below.

Small buttons

Pass size="sm" for compact contexts (toolbars, dense forms, table action columns).

Call to action

Pass size="lg" for prominent CTAs (hero sections, marketing tiles).

Block level

block=true stretches the button to fill its container width. Common inside modal footers and login forms.

Progress indicators

isLoading=true adds pf-m-progress / pf-m-in-progress and renders the PF v6 spinner inside a __progress span. Pair with spinnerLabel for screen readers.

Pass href="..." to render an <a> with button styling. Combine with icon + iconPosition="end" for the common "open external" pattern.

component="span" variant="link" inline=true renders the styled element as a <span> — useful inside running prose where <a> or <button> would be invalid (e.g. already inside another anchor).

Inline link as a span renders the button-styled element directly inside running text without breaking the line: click here to learn more . Use this when you can't nest a real <a> or <button> (e.g. inside another anchor) but still want the link styling.

Custom component

component="span" / "div" / etc. replaces the host element. Use sparingly — the host element loses its native button semantics (no Enter/Space activation, no implicit role) unless you reintroduce them yourself.

Span as a button
Div as a button

Aria-disabled examples

ariaDisabled=true communicates the disabled state to assistive tech without setting the HTML disabled attribute — the button stays focusable, so tooltips and pointer handlers still work.

Button with count

Pass count to render a PF v6 badge on the trailing edge of the button. countRead=true switches the badge to the muted (read) treatment.

Plain with no padding

hasNoPadding=true strips the inline padding on variant="plain" buttons. Useful when a plain icon button sits inside another inline element and you want the icon flush.

The right-hand button has asNoPadding() — the icon sits flush.

Stateful

variant="stateful" + stateful=true + state="read"|"unread"|"attention" renders a notification-style icon button that reflects the current state via PF's stateful tokens.

Circle buttons

circle=true renders a circular icon-only button (pf-m-circle). Combine it with any variant — including the stateful read / unread / attention states. Always pass ariaLabel.

Button types

type="submit" / "reset" set the form-control behaviour of a <button>; the default is "button" (no implicit form action).

Progress button (login demo)

A submit button that indicates progress while an HTMX request is in flight, then swaps in a success state. The endpoint is a server-side stub — credentials aren't checked and the read-only inputs can't be edited. HTMX makes the request; Alpine toggles pf-m-in-progress while it runs.

Live demo — this example is server-driven; run the showcase locally for the interactive version.

Favorite

favorite=true turns the button into a star toggle. favorited=true switches to the filled state — PF v6 cross-fades the outline / filled star automatically via the two __icon-favorite / __icon-favorited sub-spans.

Settings

settings=true renders a gear icon with PF v6's rotate-on-hover transition. Always pair with ariaLabel since the button is icon-only.

Hover the button to watch the gear rotate (PF v6 ships the transition).

Hamburger

hamburger=true renders the three-line menu icon. Add hamburgerVariant="expand"|"collapse" to morph between the bars and an arrow. Wire to a real toggle via Alpine.js to drive ariaExpanded.

Clicked buttons

The persistent pf-m-clicked treatment marking a currently-applied action.

Documentation

Button props

Parameters accepted by {#include components/actions/button ... /}. Model mode: pass btn=<Button> built in Java instead — the Java tab on each example shows the builder. The content slot wraps button text in pf-v6-c-button__text; skip the slot for icon-only buttons (favorite / settings / hamburger).

* required
Name Type Default Description
* id String DOM id.
variant "primary" | "secondary" | "tertiary" | "danger" | "warning" | "link" | "plain" | "control" | "stateful" "primary" Visual treatment. Not applied when hamburger / favorite / settings is set — those imply pf-m-plain.
size "sm" | "lg" default Compact (sm) or call-to-action (lg) sizing. Omit for the standard size.
circle boolean false Renders a circular icon-only button (pf-m-circle). Pair with icon + ariaLabel.
block boolean false Stretches the button to fill its container width.
inline boolean false Strips block-level padding (only meaningful for variant="link").
disabled / ariaDisabled boolean false disabled sets the HTML disabled attribute (non-focusable, no events). ariaDisabled sets aria-disabled only (still focusable, useful with tooltips).
icon / iconPosition String "set:slug" / "start" | "end" — / "start" Renders the icon inside pf-v6-c-button__icon with pf-m-start or pf-m-end when the button also has text. Omit content to make an icon-only button (pair with ariaLabel) — the spacing modifier is omitted, matching PF core.
isLoading / spinnerLabel boolean / String false / — Adds pf-m-progress + pf-m-in-progress and renders the PF v6 spinner inside __progress. spinnerLabel becomes the spinner's aria-label.
count / countRead Integer / boolean — / false Renders a PF v6 badge on the trailing edge with the count value. countRead=true switches to the muted (read) treatment.
component / href / type Tag string / URL / "submit" | "reset" "button" / — / "button" Element override. href implies <a>; component forces a specific tag. type applies to <button> only.
ariaLabel / ariaExpanded String / "true" | "false" Accessibility hooks. Required for icon-only buttons. ariaExpanded drives the open/closed indication on hamburger and disclosure buttons.
clicked / hasNoPadding boolean false clicked applies active-state styling. hasNoPadding strips the plain variant's inline padding.
stateful / state boolean / "read" | "unread" | "attention" Stateful button for notification-style toggles. Pair variant="stateful" with stateful=true and a state value.
favorite / favorited boolean false Star-toggle button. Implies pf-m-plain. favorited=true shows the filled state.
settings boolean false Gear-icon button with rotate-on-hover. Implies pf-m-plain.
hamburger / hamburgerVariant boolean / "expand" | "collapse" false / — Three-line menu button. Implies pf-m-plain. hamburgerVariant morphs the icon between bars and an arrow for nav drawers.
content (slot) Qute insert Button text. Wrapped in pf-v6-c-button__text. Omit for icon-only buttons.

Divergence from PF React. We skip ouiaId / ouiaSafe (testing tooling we don't use), className (React-specific), inoperableEvents (managed at the consumer level here), tabIndex, spinnerAriaLabelledBy, and spinnerAriaValueText (the latter two are subsumed by the spinner component's own props if you compose by hand). PF's countOptions object is flattened into count + countRead.

Usage

Variant precedence. The specialised flags (favorite, settings, hamburger) imply their own plain treatment and override variant. Don't set both — pick one.

Disabled vs. aria-disabled. Use disabled=true for buttons that are genuinely non-interactive (form submit while the form is invalid, action that requires another step first). Use ariaDisabled=true when you still want pointer / focus events to fire — e.g. when a tooltip needs to explain why the button is unavailable.

Icon-only buttons need a label. Plain / favorite / settings / hamburger buttons don't have visible text. PF v6's HTML smoke tests assert each button has an accessible name — always pass ariaLabel (or wire ariaLabelledBy via the consumer template).

Component override caveats. Setting component="span" (or another non-interactive tag) strips the native button semantics — no Enter/Space activation, no role, no tab-order. Use only when the host element constrains your choice (inline link inside an anchor, span inside a list). Re-add role="button" and keyboard handlers yourself if you need them.