Dots

Action Bar

The bottom bar with the main action of a screen, plus floating action pills for quick actions, selection and undo.

Total with fee$101.50
import {
  ActionBar,
  ActionBarActions,

The ActionBar holds the main action of a screen: an optional summary line such as the total to pay, a short hint, and one to three buttons. It sits at the bottom of a form, a review screen or a sheet and keeps clear of the home indicator. Use ActionPill instead when the actions float over content, such as quick actions on a list, a selection bar or an undo pill.

Installation

pnpm dlx shadcn@latest add @dots/action-bar

Usage

import {
  ActionBar,
  ActionBarActions,
  ActionBarHint,
  ActionBarSummary,
} from "@/components/ui/action-bar"
import { Button } from "@/components/ui/button"
<ActionBar>
  <ActionBarSummary label="Total with fee">$101.50</ActionBarSummary>
  <ActionBarActions>
    <Button size="lg">Pay $101.50</Button>
  </ActionBarActions>
</ActionBar>

Composition

Use the following composition to build an action bar:

ActionBar
├── ActionBarSummary
├── ActionBarHint
└── ActionBarActions
    └── Button

Use the following composition to build floating pills:

ActionPillDock
└── ActionPill
    ├── ActionPillLabel
    └── ActionPillButton

Features

  • Sticky, fixed or static placement with one position prop
  • staticFrom keeps one bar sticky on phones and in flow from md or lg up
  • Bottom padding for the safe area, plus a --action-bar-inset variable for host app insets
  • A summary line for a label and a value, such as the total to pay
  • A hint in three tones, for a quiet note, something to check, or the reason an action is blocked
  • Text buttons share the width; icon buttons keep their size
  • Floating pills built on the React Aria toolbar, with a polite status region for live labels

Action bar vs action pill

Use ActionBar for the one main action of a screen, such as pay, send or continue. It takes the full width and stays at the bottom.

Use ActionPill for actions that float over content and should not take a full row, such as quick actions on a home screen, the actions for selected rows, or undo after a bulk change.

Sticky

By default the bar is sticky: it pins to the bottom of its scroll container while the content scrolls under it. Use position="fixed" to pin it to the window, or position="static" to leave it in flow, for example in a sheet footer.

Recipient
Maria Lopez
Account
•••• 4821
You send
$250.00
Fee
$1.50
Exchange rate
1 USD = 0.92 EUR
They get
€230.00
Arrives
Today, within minutes
Reference
Rent, March
Total$251.50
import {
  ActionBar,
  ActionBarActions,

Set staticFrom="md" or staticFrom="lg" to render one bar that is sticky on phones and sits at the end of a form or side panel on wide screens.

<ActionBar staticFrom="md">
  <ActionBarActions>
    <Button size="lg">Send $251.50</Button>
  </ActionBarActions>
</ActionBar>

Blocked

When the main action cannot go ahead, say why in an ActionBarHint with variant="destructive" above the buttons and offer the ways out. Point each button's aria-describedby at the hint so the reason is read with the button.

Not enough balance. Add money or lower the amount.

"use client"

import * as React from "react"

Irreversible

Use variant="warning" for something to check before an action that cannot be undone, and the destructive button variant for the action itself.

This is permanent. The card stops working right away.

import {
  ActionBar,
  ActionBarActions,

Pending

Pass isPending to the button while the action runs. React Aria keeps focus on the button and blocks repeat presses. A default hint below the buttons can say what to expect.

Keep the app open, this takes a few seconds.

"use client"

import * as React from "react"

Floating pill

Wrap ActionPill in ActionPillDock to float it above the bottom edge. Clicks pass through the empty space around the pills. Use the primary variant for the main action of a screen.

"use client"
import {
  ActionPill,

Selection and undo

Use the glass variant for a selection bar and for undo. Mark the count with ActionPillLabel live so it is announced when it appears and when it changes. Keep the dock mounted while the pills swap, because it holds the status region.

"use client"

import * as React from "react"

Accessibility

ActionBar is a plain container; its buttons are the kit Button. ActionPill renders a React Aria Toolbar, so arrow keys move between its buttons and Tab leaves the pill. ActionPillButton renders a React Aria Button with press, focus and disabled handling.

Name every pill

ActionPill requires an aria-label or aria-labelledby, for example "Quick actions" or "Selected payments".

<ActionPill variant="glass" aria-label="Selected payments">
  <ActionPillLabel live>3 selected</ActionPillLabel>
  {/* buttons */}
</ActionPill>

Label icon-only buttons

Give every ActionPillButton with size="icon" an aria-label, and every icon-only Button in the bar.

Tie the reason to the button

Give ActionBarHint an id and set aria-describedby on the buttons it explains. A disabled button with no reason is a dead end; prefer a button label that says what is missing, such as "Enter an amount".

Keep focus when pills swap

When a press replaces one pill with another, the pressed button unmounts. Put autoFocus on a button of the next pill, only after a user action, so keyboard focus is not lost.

API Reference

ActionBar

The bottom bar. Renders a <div>.

PropTypeDefaultDescription
position"sticky" | "fixed" | "static""sticky"Pin to the scroll container, pin to the window, or stay in flow.
staticFrom"md" | "lg"-From this breakpoint up, behave like static. Ignored when static.
classNamestring-Additional classes to apply to the bar.
...propsReact.ComponentProps<"div">-Props spread to the root element.

ActionBarSummary

A label and a value above the buttons.

PropTypeDefaultDescription
labelReact.ReactNode-Required. What the value is.
childrenReact.ReactNode-Required. The value, usually a money amount.
classNamestring-Additional classes to apply.

ActionBarHint

One short line about the action. Renders a <p>.

PropTypeDefaultDescription
variant"default" | "warning" | "destructive""default"A quiet note, something to check, or why the action is blocked.
...propsReact.ComponentProps<"p">-Props spread to the paragraph, such as id.

ActionBarActions

The row of buttons. Put the main action last.

PropTypeDefaultDescription
classNamestring-Additional classes to apply.
...propsReact.ComponentProps<"div">-Props spread to the row element.

ActionPillDock

Floats pills above the bottom edge and holds the polite status region for live labels.

PropTypeDefaultDescription
position"sticky" | "fixed" | "absolute""sticky"Float over a scroll container, the window, or a positioned parent.
justify"center" | "between""center""between" puts the first pill at the start and the last at the end.
classNamestring-Additional classes to apply.

ActionPill

A floating pill of actions. Renders a React Aria Toolbar and accepts its props except orientation.

PropTypeDefaultDescription
aria-label or aria-labelledbystring-Required. Names the pill.
variant"primary" | "glass""primary"primary for the main action, glass for secondary pills, selection and undo.
classNamestring-Additional classes to apply.

ActionPillButton

A button inside a pill. Renders a React Aria Button and accepts its props.

PropTypeDefaultDescription
size"default" | "sm" | "icon""default"icon is a round icon-only button; give it an aria-label.
variant"default" | "destructive""default"destructive for an irreversible action.
classNamestring-Additional classes to apply.

ActionPillLabel

Text inside a pill. Renders a <span>.

PropTypeDefaultDescription
livebooleanfalseAnnounce the text when it appears and changes. Inside a dock it speaks through the dock's region.
...propsReact.ComponentProps<"span">-Props spread to the span.