v1.0

FAB Button

Floating action button for a screen's primary action, with an optional expandable group of secondary actions that stack vertically or fan out radially.

Pro

Description

FabButton is a floating action button (FAB) for the single most important action on a screen — compose, create, add. On its own it is a compact button with an icon and an optional text label. Wrap a FabGroup around it and the button becomes a speed-dial trigger that reveals a small cluster of secondary actions.

FabGroup is a compound, context-driven component. The root (FabGroup) owns the open state and the shared variant, appearance, size, and placement; FabGroupTrigger is the always-visible toggle; FabGroupList wraps one FabGroupItem per action. Items either stack vertically with a visible label, or fan out on an arc as icon-only buttons, depending on placement.

Reach for it for a persistent, high-emphasis action that floats above content — typically pinned to a bottom corner of the viewport. For a long or text-heavy menu, use a DropdownMenu; for an action that sits inline in the page rather than floating over it, use a regular Button.

FabButton is a Create UI Pro component. With a Pro seat, npx @create-ui/cli add fab-button installs it. The previews below are marked with a Pro badge.

Installation

pnpm dlx @create-ui/cli add fab-button

Anatomy

Use FabButton alone for a single action. For a speed dial, nest the trigger and list inside FabGroup, which hands variant, appearance, size, and placement down to its parts.

<FabButton leading={<RiAddLine />}>Create</FabButton>
<FabGroup placement="vertical">
  <FabGroupTrigger leading={<RiAddLine />} />
  <FabGroupList>
    <FabGroupItem icon={<RiUploadCloud2Line />}>Upload file</FabGroupItem>
    <FabGroupItem icon={<RiFolderAddLine />}>New project</FabGroupItem>
  </FabGroupList>
</FabGroup>

FabGroup is position: relative and its items are absolutely positioned around the trigger, so you place the group where it should live (for example fixed bottom-6 right-6); it does not fix or portal itself.

Usage

import {
  FabButton,
  FabGroup,
  FabGroupItem,
  FabGroupList,
  FabGroupTrigger,
} from "@/components/ui/fab-button"
<FabButton variant="primary" leading={<RiAddLine />}>
  Create
</FabButton>

The group is uncontrolled by default — pass defaultOpen for the initial state, or drive it with open and onOpenChange:

<FabGroup open={open} onOpenChange={setOpen} placement="vertical">
  <FabGroupTrigger leading={<RiAddLine />} />
  <FabGroupList>
    <FabGroupItem icon={<RiUploadCloud2Line />} onSelect={upload}>
      Upload file
    </FabGroupItem>
  </FabGroupList>
</FabGroup>

Examples

Every example is a Pro preview (marked with a Pro badge). The API is the same across them; only the props change.

Variants

variant sets the semantic color shared by the trigger and its actions: primary for the accent action, neutral for a quiet control, and inverse for inverted or dark surfaces. Because inverse is tuned for dark backgrounds it reads as near-invisible on a light one, so the preview renders it on a dark tile. Each variant below is an open speed dial — click a trigger to toggle it.

Pro
primary
neutral
inverse

Appearance

appearance controls the fill weight. In a group it sets the fill of the actions — default is a solid fill, soft is a tinted alpha fill that sits more quietly over content; the trigger always stays solid.

Pro
default
soft

Sizes

size scales the padding, icon, and corner radius. lg is the default floating size; md is tighter for denser layouts or nested toolbars. In a group the trigger follows size while the actions stay md.

Pro
md
lg

Shapes

shape switches between a size-scaled rounded rectangle (rounded) and a fully circular pill. A lone icon-only FAB usually wants pill; a labelled FAB reads better as rounded. Shape applies to a standalone FabButton — inside a FabGroup the trigger and actions are always pill.

Pro

FAB group

Wrap the trigger and a FabGroupList in FabGroup for a speed dial. With placement="vertical" the actions stack above the trigger, each with its icon and a visible label. The trigger's leading icon cross-fades to a close (×) glyph while the group is open.

Pro

Radial menu

The center, left, and right placements fan the items out on an arc around the trigger as icon-only buttons — the children text becomes each item's aria-label. center supports up to 5 items; left and right support up to 3. The preview shows each placement at its maximum count.

Pro
center · 5 (max)
left · 3 (max)
right · 3 (max)

Set asChild to render the FAB as another element — for example a Next.js Link — while keeping the button styling. In asChild mode you own the children, so include the icon yourself.

<FabButton asChild>
  <a href="/compose">
    <RiAddLine />
  </a>
</FabButton>

Accessibility

FabButton renders a real <button type="button">. In asChild mode the rendered element receives aria-disabled and tabIndex={-1} when disabled, since a custom element cannot be natively disabled.

FabGroupTrigger is a button with aria-haspopup, aria-expanded reflecting the open state, and aria-controls pointing at the list. It carries a default aria-label ("Open actions" / "Close actions") that you can override.

KeyDescription
TabMoves focus to the trigger and, when open, each action button.
Space EnterActivates the focused button (toggles the group or runs an action).
EscapeCloses an open group and returns focus to the trigger.

ARIA notes:

  • FabGroupList has role="group" and is set inert while closed, so its actions are removed from the tab order and the accessibility tree until the group opens.
  • A pointer press outside the group closes it.
  • In the radial placements items are icon-only; pass the label as children so it becomes the button's aria-label. In the vertical placement the label is visible text.

Styling

Tailwind override: pass className to merge Tailwind classes with the component's CVA classes (via cn()):

<FabButton className="fixed right-6 bottom-6" leading={<RiAddLine />} />

Data slots and attributes: the components set these for CSS targeting:

  • data-slot="fab-button" on the button, with data-variant, data-appearance, and data-size.
  • data-slot="fab-button-icon" on the leading icon wrapper and data-slot="fab-button-label" on the text label.
  • data-slot="fab-group" on the root, with data-state (open / closed), data-placement, data-variant, data-appearance, and data-size.
  • data-slot="fab-group-trigger" on the toggle button.
  • data-slot="fab-group-list" on the list, with data-state.
  • data-slot="fab-group-item" on each item wrapper, with data-state.

Target the open state in CSS:

[data-slot="fab-group"][data-state="open"] {
  /* ... */
}
  • Button: the standard in-flow button for actions that live inside the page rather than floating over it.
  • Close Button: the icon-only dismiss control used elsewhere; FabGroupTrigger shows a similar close glyph when open.
  • Dropdown Menu: use this for a longer or text-heavy menu of actions instead of a radial fan.
  • Tooltip: pair with icon-only FABs to label them on hover.

API Reference

FabButton

The floating action button. Renders a <button type="button"> (or the child element when asChild) and extends React.ComponentProps<"button">, so standard button attributes (onClick, aria-*, etc.) are accepted.

Props

PropTypeDefaultDescription
leadingReact.ReactNode-Icon rendered before the label; sized by the component per size.
asChildbooleanfalseRender the child element instead of a <button> via Radix Slot.
disabledbooleanfalseDisables the button (native when a <button>, aria-disabled when asChild).
classNamestring-Tailwind classes merged with the component's CVA classes via cn().
childrenReact.ReactNode-Optional text label (or, with asChild, the element to render).

Variants

VariantOptionsDefaultDescription
variant"primary" "neutral" "inverse""primary"Semantic color intent (inverse for dark surfaces).
appearance"default" "soft""default"Fill weight — solid or tinted.
size"md" "lg""lg"Padding, icon, and radius scale.
shape"rounded" "pill""rounded"Size-scaled rounded rectangle or a full circle.

Also exported: fabButtonVariants — the CVA function for composing custom elements.

FabGroup

Root of the speed dial. Owns the open state and provides the shared context to its parts. Renders a <div> and extends Omit<React.ComponentProps<"div">, "onChange">.

Props

PropTypeDefaultDescription
placement"center" "left" "right" "vertical""center"Vertical stack of labelled items, or a radial fan of icons.
variant"primary" "neutral" "inverse""primary"Color intent shared by the trigger and items.
appearance"default" "soft""soft"Fill weight of the items (the trigger is always default).
size"md" "lg""lg"Trigger size; items are always md.
openboolean-Controlled open state.
defaultOpenbooleanfalseUncontrolled initial open state.
onOpenChange(open: boolean) => void-Fires whenever the open state changes.
classNamestring-Tailwind classes merged via cn().
childrenReact.ReactNode-The trigger and list.

FabGroupTrigger

The always-visible toggle. Inherits variant and size from the group and is forced to appearance="default" and shape="pill". Extends FabButton props minus variant / appearance / size / shape / children.

Props

PropTypeDefaultDescription
leadingReact.ReactNode-The resting icon; cross-fades to a close glyph while open.
aria-labelstringderived from stateOverrides the default "Open actions" / "Close actions" label.
onClick(e) => void-Runs before the group toggles.
classNamestring-Tailwind classes merged via cn().

FabGroupList

Wraps the items and positions them per the group's placement. Sets role="group" and is inert while closed. Renders a <div> and extends React.ComponentProps<"div">.

Props

PropTypeDefaultDescription
classNamestring-Tailwind classes merged via cn().
childrenReact.ReactNode-The FabGroupItems.

FabGroupItem

A single action. Inherits variant / appearance from the group and is always size="md", shape="pill". Extends FabButton props minus variant / appearance / size / shape / leading.

Props

PropTypeDefaultDescription
iconReact.ReactNode-The item's icon.
onSelect() => void-Runs when the item is activated (alongside any onClick).
classNamestring-Tailwind classes merged via cn().
childrenReact.ReactNode-Visible label in the vertical placement; used as the aria-label in the radial placements.