Action Bar

Action Bar is used to display a bottom action bar with a set of actions for selected items.

Source
Theme Source
pnpm dlx dreamy add action-bar

Action Bar is a component that displays a set of actions at the bottom of the screen, typically used for bulk operations on selected items.

Show Action Bar
function ControlledActionBar() {
	const { isOpen, onToggle, onClose } = useControllable();
 
	return (
		<>
			<Switch size={"sm"} isChecked={isOpen} onChangeValue={onToggle}>
				Show Action Bar
			</Switch>
 
			<ActionBar.Root isOpen={isOpen} onClose={onClose}>
				<ActionBar.Content>
					<ActionBar.SelectionTrigger>2 items selected</ActionBar.SelectionTrigger>
					<ActionBar.Separator />
					<Button size="sm" variant="outline" rightIcon={<LuTrash />}>
						Delete
					</Button>
					<Button size="sm" variant="outline" rightIcon={<LuShare />}>
						Share
					</Button>
				</ActionBar.Content>
			</ActionBar.Root>
		</>
	);
}

Add a close button to allow users to dismiss the action bar manually.

Show Action Bar
function ActionBarWithClose() {
	const { isOpen, onToggle, onClose } = useControllable();
 
	return (
		<>
			<Switch isChecked={isOpen} onChangeValue={onToggle} size={"sm"}>
				Show Action Bar
			</Switch>
 
			<ActionBar.Root isOpen={isOpen} onClose={onClose}>
				<ActionBar.Content>
					<ActionBar.SelectionTrigger>5 items selected</ActionBar.SelectionTrigger>
					<ActionBar.Separator />
					<Button rightIcon={<LuSquarePlus />} size="sm" variant="outline">
						Add to collection
					</Button>
					<Button color="error" rightIcon={<LuTrash />} size="sm">
						Delete items
					</Button>
					<ActionBar.CloseTrigger />
				</ActionBar.Content>
			</ActionBar.Root>
		</>
	);
}

You can include multiple action buttons to provide various operations.

Show Action Bar
function ActionBarMultiple() {
	const { isOpen, onToggle, onClose } = useControllable();
 
	return (
		<>
			<Switch isChecked={isOpen} onChangeValue={onToggle} size={"sm"}>
				Show Action Bar
			</Switch>
 
			<ActionBar.Root isOpen={isOpen} onClose={onClose}>
				<ActionBar.Content>
					<ActionBar.SelectionTrigger>12 files selected</ActionBar.SelectionTrigger>
					<ActionBar.Separator />
					<Button rightIcon={<LuDownload />} size="sm" variant="outline">
						Download
					</Button>
					<Button rightIcon={<LuShare />} size="sm" variant="outline">
						Share
					</Button>
					<Button rightIcon={<LuFolderPlus />} size="sm" variant="outline">
						Move to folder
					</Button>
					<ActionBar.Separator />
					<Button color="error" rightIcon={<LuTrash />} size="sm">
						Delete
					</Button>
				</ActionBar.Content>
			</ActionBar.Root>
		</>
	);
}

You can change the size of the action bar using the size prop.

Small

Show sm Action Bar

Medium

Show md Action Bar

Large

Show lg Action Bar
function ActionBarSizes({ size = "md" }: { size?: ActionBarVariantProps["size"] }) {
	const { isOpen, onToggle, onClose } = useControllable();
 
	return (
		<>
			<Switch isChecked={isOpen} onChangeValue={onToggle} size={"sm"}>
				Show {size} Action Bar
			</Switch>
 
			<ActionBar.Root isOpen={isOpen} onClose={onClose} size={size}>
				<ActionBar.Content>
					<ActionBar.SelectionTrigger>2 selected</ActionBar.SelectionTrigger>
					<ActionBar.Separator />
					<Button color="error" size={size} variant="outline">
						Delete
					</Button>
					<ActionBar.CloseTrigger size={size} />
				</ActionBar.Content>
			</ActionBar.Root>
		</>
	);
}

A common use case is integrating the action bar with table selections.

NameAgeGender
John Doe20Male
Jane Doe22Female
Jim Doe25Male
interface User {
	id: number;
	name: string;
	age: number;
	gender: string;
	isSelected: boolean;
}
 
function ActionBarTable() {
	const [users, setUsers] = useState<User[]>([
		{ id: 1, name: "John Doe", age: 20, gender: "Male", isSelected: false },
		{ id: 2, name: "Jane Doe", age: 22, gender: "Female", isSelected: false },
		{ id: 3, name: "Jim Doe", age: 25, gender: "Male", isSelected: false }
	]);
 
	const { isOpen, onClose } = useControllable({
		isOpen: users.some((user) => user.isSelected),
		onClose: () => setUsers((prev) => prev.map((user) => ({ ...user, isSelected: false })))
	});
 
	const toggleSelection = useCallback((id: number) => {
		setUsers((prev) =>
			prev.map((user) => (user.id === id ? { ...user, isSelected: !user.isSelected } : user))
		);
	}, []);
 
	const unselectAll = useCallback(() => {
		setUsers((prev) => prev.map((user) => ({ ...user, isSelected: false })));
	}, []);
 
	const toggleSelectionAll = useCallback(() => {
		setUsers((prev) =>
			prev.map((user) => ({ ...user, isSelected: !prev.every((user) => user.isSelected) }))
		);
	}, []);
 
	return (
		<>
			<Table.Root variant={"simple"} w="full" withBackground>
				<Table.Table>
					<Table.Header>
						<Table.Row>
							<Table.ColumnHeader w={"60px"}>
								<Checkbox
									isChecked={users.every((user) => user.isSelected)}
									isIndeterminate={
										users.some((user) => user.isSelected) &&
										!users.every((user) => user.isSelected)
									}
									onChangeValue={toggleSelectionAll}
								/>
							</Table.ColumnHeader>
							<Table.ColumnHeader>Name</Table.ColumnHeader>
							<Table.ColumnHeader>Age</Table.ColumnHeader>
							<Table.ColumnHeader>Gender</Table.ColumnHeader>
						</Table.Row>
					</Table.Header>
					<Table.Body>
						{users.map((user, index) => (
							<Table.Row
								/* apply border radiuses to first and last rows in the cells */
								_first={{
									"& > td:first-of-type": {
										borderStartStartRadius: "l2"
									},
									"& > td:last-of-type": {
										borderStartEndRadius: "l2"
									}
								}}
								_hover={{
									bg: "alpha.50"
								}}
								_last={{
									"& > td:first-of-type": {
										borderEndStartRadius: "l2"
									},
									"& > td:last-of-type": {
										borderEndEndRadius: "l2"
									}
								}}
								bg={user.isSelected ? "alpha.50" : "transparent"}
								cursor={"pointer"}
								key={index}
								onClick={() => toggleSelection(user.id)}
							>
								<Table.Cell w={"60px"}>
									<Checkbox
										isChecked={user.isSelected}
										onChangeValue={() => toggleSelection(user.id)}
									/>
								</Table.Cell>
								<Table.Cell>{user.name}</Table.Cell>
								<Table.Cell>{user.age}</Table.Cell>
								<Table.Cell>{user.gender}</Table.Cell>
							</Table.Row>
						))}
					</Table.Body>
				</Table.Table>
			</Table.Root>
 
			<ActionBar.Root isOpen={isOpen} onClose={onClose}>
				<ActionBar.Content>
					<ActionBar.SelectionTrigger>
						{users.filter((user) => user.isSelected).length} selected
					</ActionBar.SelectionTrigger>
					<ActionBar.Separator />
					<Button size="sm" variant="outline">
						Download
					</Button>
					<Button
						color="error"
						onClick={() => {
							unselectAll();
							onClose();
						}}
						rightIcon={<LuTrash />}
						size="sm"
						variant="outline"
					>
						Delete
					</Button>
				</ActionBar.Content>
			</ActionBar.Root>
		</>
	);
}

You can use the action bar in an uncontrolled mode with defaultIsOpen.

<ActionBar.Root defaultIsOpen onClose={() => console.log('closed')}>
  <ActionBar.Positioner>
    <ActionBar.Content>
      <ActionBar.SelectionTrigger>
        1 selected
      </ActionBar.SelectionTrigger>
      <ActionBar.Separator />
      <Button size="sm" variant="outline">
        Action
      </Button>
      <ActionBar.CloseTrigger>
        <CloseButton size="sm" />
      </ActionBar.CloseTrigger>
    </ActionBar.Content>
  </ActionBar.Positioner>
</ActionBar.Root>