ActionBar

A fixed-position bottom bar for bulk selection actions

Usage

ActionBar is a fixed-position bottom bar that appears when items are selected, providing contextual bulk actions. It is built using Affix, Transition and Paper components.

ActionBar content can be any React node. Use ActionBar.Divider to add a vertical divider and ActionBar.CloseButton to add a close button that calls onClose.

ActionBar root has role="group". Set aria-label to describe what the actions apply to.

Element positionElement nameSymbolAtomic mass
6CarbonC12.011
7NitrogenN14.007
39YttriumY88.906
56BariumBa137.33
58CeriumCe140.12
import { useState } from 'react';
import { ActionBar, Button, Checkbox, Table, Text } from '@mantine/core';

const elements = [
  { position: 6, mass: 12.011, symbol: 'C', name: 'Carbon' },
  { position: 7, mass: 14.007, symbol: 'N', name: 'Nitrogen' },
  { position: 39, mass: 88.906, symbol: 'Y', name: 'Yttrium' },
  { position: 56, mass: 137.33, symbol: 'Ba', name: 'Barium' },
  { position: 58, mass: 140.12, symbol: 'Ce', name: 'Cerium' },
];

function Demo() {
  const [selection, setSelection] = useState<number[]>([]);

  const toggleRow = (position: number) =>
    setSelection((current) =>
      current.includes(position)
        ? current.filter((item) => item !== position)
        : [...current, position]
    );

  const toggleAll = () =>
    setSelection((current) =>
      current.length === elements.length ? [] : elements.map((element) => element.position)
    );

  const rows = elements.map((element) => (
    <Table.Tr
      key={element.position}
      bg={selection.includes(element.position) ? 'var(--mantine-color-blue-light)' : undefined}
    >
      <Table.Td>
        <Checkbox
          aria-label="Select row"
          checked={selection.includes(element.position)}
          onChange={() => toggleRow(element.position)}
        />
      </Table.Td>
      <Table.Td>{element.position}</Table.Td>
      <Table.Td>{element.name}</Table.Td>
      <Table.Td>{element.symbol}</Table.Td>
      <Table.Td>{element.mass}</Table.Td>
    </Table.Tr>
  ));

  return (
    <>
      <Table>
        <Table.Thead>
          <Table.Tr>
            <Table.Th>
              <Checkbox
                aria-label="Select all"
                checked={selection.length === elements.length}
                indeterminate={selection.length > 0 && selection.length !== elements.length}
                onChange={toggleAll}
              />
            </Table.Th>
            <Table.Th>Element position</Table.Th>
            <Table.Th>Element name</Table.Th>
            <Table.Th>Symbol</Table.Th>
            <Table.Th>Atomic mass</Table.Th>
          </Table.Tr>
        </Table.Thead>
        <Table.Tbody>{rows}</Table.Tbody>
      </Table>

      <ActionBar opened={selection.length > 0} onClose={() => setSelection([])} shadow="md">
        <Text size="sm">{selection.length} selected</Text>
        <ActionBar.Divider />
        <Button variant="default" size="compact-sm">
          Delete
        </Button>
        <Button variant="default" size="compact-sm">
          Move
        </Button>
        <Button variant="default" size="compact-sm">
          Archive
        </Button>
        <ActionBar.CloseButton />
      </ActionBar>
    </>
  );
}

With ActionIcon buttons

Element positionElement nameSymbolAtomic mass
6CarbonC12.011
7NitrogenN14.007
39YttriumY88.906
56BariumBa137.33
58CeriumCe140.12
import { useState } from 'react';
import { ArchiveIcon, FolderIcon, TrashIcon } from '@phosphor-icons/react';
import { ActionBar, ActionIcon, Checkbox, Group, Table, Text, Tooltip } from '@mantine/core';

const elements = [
  { position: 6, mass: 12.011, symbol: 'C', name: 'Carbon' },
  { position: 7, mass: 14.007, symbol: 'N', name: 'Nitrogen' },
  { position: 39, mass: 88.906, symbol: 'Y', name: 'Yttrium' },
  { position: 56, mass: 137.33, symbol: 'Ba', name: 'Barium' },
  { position: 58, mass: 140.12, symbol: 'Ce', name: 'Cerium' },
];

function Demo() {
  const [selection, setSelection] = useState<number[]>([]);

  const toggleRow = (position: number) =>
    setSelection((current) =>
      current.includes(position)
        ? current.filter((item) => item !== position)
        : [...current, position]
    );

  const toggleAll = () =>
    setSelection((current) =>
      current.length === elements.length ? [] : elements.map((element) => element.position)
    );

  const rows = elements.map((element) => (
    <Table.Tr
      key={element.position}
      bg={selection.includes(element.position) ? 'var(--mantine-color-blue-light)' : undefined}
    >
      <Table.Td>
        <Checkbox
          aria-label="Select row"
          checked={selection.includes(element.position)}
          onChange={() => toggleRow(element.position)}
        />
      </Table.Td>
      <Table.Td>{element.position}</Table.Td>
      <Table.Td>{element.name}</Table.Td>
      <Table.Td>{element.symbol}</Table.Td>
      <Table.Td>{element.mass}</Table.Td>
    </Table.Tr>
  ));

  return (
    <>
      <Table>
        <Table.Thead>
          <Table.Tr>
            <Table.Th>
              <Checkbox
                aria-label="Select all"
                checked={selection.length === elements.length}
                indeterminate={selection.length > 0 && selection.length !== elements.length}
                onChange={toggleAll}
              />
            </Table.Th>
            <Table.Th>Element position</Table.Th>
            <Table.Th>Element name</Table.Th>
            <Table.Th>Symbol</Table.Th>
            <Table.Th>Atomic mass</Table.Th>
          </Table.Tr>
        </Table.Thead>
        <Table.Tbody>{rows}</Table.Tbody>
      </Table>

      <ActionBar opened={selection.length > 0} onClose={() => setSelection([])}>
        <Text size="sm" fw={500}>{selection.length} rows selected</Text>
        <ActionBar.Divider />
        <Tooltip.Group openDelay={300} closeDelay={100}>
          <Group gap="xs">
            <Tooltip label="Delete">
              <ActionIcon variant="default" color="red" size="lg">
                <TrashIcon size={16} />
              </ActionIcon>
            </Tooltip>
            <Tooltip label="Move to folder">
              <ActionIcon variant="default" size="lg">
                <FolderIcon size={16} />
              </ActionIcon>
            </Tooltip>
            <Tooltip label="Archive">
              <ActionIcon variant="default" size="lg">
                <ArchiveIcon size={16} />
              </ActionIcon>
            </Tooltip>
          </Group>
        </Tooltip.Group>
        <ActionBar.Divider />
        <ActionBar.CloseButton />
      </ActionBar>
    </>
  );
}

Placement

By default, ActionBar is centered at the bottom of the screen. You can change the position with the position prop. When using non-centered positions, set styles={{ root: { marginInline: 0 } }} to remove the auto centering.

import { useDisclosure } from '@mantine/hooks';
import { ActionBar, Button, Group, Text } from '@mantine/core';

function Demo() {
  const [center, centerHandlers] = useDisclosure(false);
  const [left, leftHandlers] = useDisclosure(false);
  const [right, rightHandlers] = useDisclosure(false);

  return (
    <>
      <Group justify="center">
        <Button onClick={centerHandlers.toggle}>Center (default)</Button>
        <Button onClick={leftHandlers.toggle}>Left</Button>
        <Button onClick={rightHandlers.toggle}>Right</Button>
      </Group>

      <ActionBar opened={center} onClose={centerHandlers.close}>
        <Text size="sm">Center position (default)</Text>
        <ActionBar.CloseButton />
      </ActionBar>

      <ActionBar
        opened={left}
        onClose={leftHandlers.close}
        position={{ bottom: 30, left: 30 }}
        styles={{ root: { marginInline: 0 } }}
      >
        <Text size="sm">Left position</Text>
        <ActionBar.CloseButton />
      </ActionBar>

      <ActionBar
        opened={right}
        onClose={rightHandlers.close}
        position={{ bottom: 30, right: 30 }}
        styles={{ root: { marginInline: 0 } }}
      >
        <Text size="sm">Right position</Text>
        <ActionBar.CloseButton />
      </ActionBar>
    </>
  );
}

Close on Escape

By default, ActionBar does not close when Escape key is pressed. Set closeOnEscape to enable this behavior:

import { useDisclosure } from '@mantine/hooks';
import { ActionBar, Button, Group, Text } from '@mantine/core';

function Demo() {
  const [opened, { toggle, close }] = useDisclosure(false);

  return (
    <>
      <Group justify="center">
        <Button onClick={toggle}>Toggle action bar</Button>
      </Group>

      <ActionBar opened={opened} onClose={close} closeOnEscape>
        <Text size="sm">Press Escape to close this bar</Text>
        <ActionBar.Divider />
        <Button variant="default" size="compact-sm" onClick={close}>
          Close
        </Button>
      </ActionBar>
    </>
  );
}

Transitions

ActionBar is animated with the Transition component, { transition: 'pop', duration: 200 } by default. Use transitionProps to change the transition, its duration and timing function:

import { ActionBar, Button } from '@mantine/core';

function Demo() {
  return (
    <ActionBar opened transitionProps={{ transition: 'slide-up', duration: 300 }}>
      <Button>Delete</Button>
    </ActionBar>
  );
}

keepMounted

By default, the action bar is removed from the DOM when it is closed. Set keepMounted to keep it mounted, for example to preserve form state inside the bar. The hidden bar is wrapped in React Activity. To hide it with display: none instead, set transitionProps={{ keepMountedMode: 'display-none' }}:

import { ActionBar, Button } from '@mantine/core';

function Demo() {
  return (
    <ActionBar opened={false} keepMounted>
      <Button>Delete</Button>
    </ActionBar>
  );
}