Examples › Pinning arbitrary columns

In addition to pinning the first column and pinning the last column using the pinFirstColumn and pinLastColumn DataTable props, you can pin arbitrary columns to the left or right side of the table by setting the pinned property on individual column definitions.

Pinning multiple columns to the left

Set pinned: 'left' on any columns you want pinned to the left side of the table:

First name
Last name
Department
Company
City
State
Address
Mission statement
JeraldHowellIndustrialRunte IncMcAllenMA2996 Ronny MountEngage synergistic infrastructures.
KathleenRueckerComputersShanahan, Robel and BeierWest GerryKS378 Berta CrescentSynthesize customized portals.
EricaVolkmanToysGoyette IncDorthysideID8873 Mertz RapidProductize front-end web services.
CliffordOberbrunnerAutomotiveRau - O'HaraShieldsboroughMI7508 Lansdowne RoadInnovate real-time applications.
AlisonKlingJeweleryGoyette IncDorthysideID8873 Mertz RapidProductize front-end web services.
No records

Here is the code:

export function PinMultipleLeftColumnsExample() {
  return (
    <DataTable
      withTableBorder
      columns={[
        { accessor: 'firstName', noWrap: true, pinned: 'left' },
        { accessor: 'lastName', noWrap: true, pinned: 'left' },
        { accessor: 'department.name', title: 'Department' },
        { accessor: 'department.company.name', title: 'Company', noWrap: true },
        { accessor: 'department.company.city', title: 'City', noWrap: true },
        { accessor: 'department.company.state', title: 'State' },
        { accessor: 'department.company.streetAddress', title: 'Address', noWrap: true },
        { accessor: 'department.company.missionStatement', title: 'Mission statement', noWrap: true },
      ]}
      records={records}
    />
  );
}

Pinning columns to both sides

You can combine left-pinned and right-pinned columns. In this example, the first two columns are pinned to the left, and the actions column is pinned to the right. This also works with row selection:

First name
Last name
Department
Company
City
State
Address
Mission statement
Row actions
JeraldHowellIndustrialRunte IncMcAllenMA2996 Ronny MountEngage synergistic infrastructures.
KathleenRueckerComputersShanahan, Robel and BeierWest GerryKS378 Berta CrescentSynthesize customized portals.
EricaVolkmanToysGoyette IncDorthysideID8873 Mertz RapidProductize front-end web services.
CliffordOberbrunnerAutomotiveRau - O'HaraShieldsboroughMI7508 Lansdowne RoadInnovate real-time applications.
AlisonKlingJeweleryGoyette IncDorthysideID8873 Mertz RapidProductize front-end web services.
No records

Here is the code:

export function PinLeftAndRightColumnsExample() {
  const [selectedRecords, setSelectedRecords] = useState<Employee[]>([]);

  return (
    <DataTable
      withTableBorder
      columns={[
        { accessor: 'firstName', noWrap: true, pinned: 'left' },
        { accessor: 'lastName', noWrap: true, pinned: 'left' },
        { accessor: 'department.name', title: 'Department' },
        { accessor: 'department.company.name', title: 'Company', noWrap: true },
        { accessor: 'department.company.city', title: 'City', noWrap: true },
        { accessor: 'department.company.state', title: 'State' },
        { accessor: 'department.company.streetAddress', title: 'Address', noWrap: true },
        { accessor: 'department.company.missionStatement', title: 'Mission statement', noWrap: true },
        {
          accessor: 'actions',
          title: <Box mr={6}>Row actions</Box>,
          textAlign: 'right',
          pinned: 'right',
          render: () => (
            <Group gap={4} justify="right" wrap="nowrap">
              <ActionIcon size="sm" variant="subtle" color="green">
                <IconEye size={16} />
              </ActionIcon>
              <ActionIcon size="sm" variant="subtle" color="blue">
                <IconEdit size={16} />
              </ActionIcon>
              <ActionIcon size="sm" variant="subtle" color="red">
                <IconTrash size={16} />
              </ActionIcon>
            </Group>
          ),
        },
      ]}
      records={records}
      selectedRecords={selectedRecords}
      onSelectedRecordsChange={setSelectedRecords}
    />
  );
}

Interactive pinning

You can allow users to pin and unpin columns interactively by setting pinnable: true on columns. When enabled, a pin icon appears in the column header on hover. Clicking it reveals a dropdown where users can choose to pin the column to the left, right, or unpin it.

Use pinned to set the initial pinning state for pinnable columns. The pinning state is persisted in localStorage when you provide a storeColumnsKey prop to the DataTable. You can also use the resetColumnsPinning function from the useDataTableColumns hook to reset pinning to default values.

Direction:

First name
Last name
Department
Company
City
State
Address
Mission statement
JeraldHowellIndustrialRunte IncMcAllenMA2996 Ronny MountEngage synergistic infrastructures.
KathleenRueckerComputersShanahan, Robel and BeierWest GerryKS378 Berta CrescentSynthesize customized portals.
EricaVolkmanToysGoyette IncDorthysideID8873 Mertz RapidProductize front-end web services.
CliffordOberbrunnerAutomotiveRau - O'HaraShieldsboroughMI7508 Lansdowne RoadInnovate real-time applications.
AlisonKlingJeweleryGoyette IncDorthysideID8873 Mertz RapidProductize front-end web services.
No records

Here is the code:

export function InteractivePinningExample() {
  const key = 'interactive-pinning-example';
  const [direction, setDirection] = useState<'ltr' | 'rtl'>('ltr');

  const { effectiveColumns, resetColumnsPinning } = useDataTableColumns<Employee>({
    key,
    columns: [
      { accessor: 'firstName', noWrap: true, pinnable: true, pinned: 'left' },
      { accessor: 'lastName', noWrap: true, pinnable: true },
      { accessor: 'department.name', title: 'Department', pinnable: true },
      { accessor: 'department.company.name', title: 'Company', noWrap: true, pinnable: true },
      { accessor: 'department.company.city', title: 'City', noWrap: true, pinnable: true },
      { accessor: 'department.company.state', title: 'State', pinnable: true },
      { accessor: 'department.company.streetAddress', title: 'Address', noWrap: true, pinnable: true },
      { accessor: 'department.company.missionStatement', title: 'Mission statement', noWrap: true, pinnable: true },
    ],
  });

  return (
    <Stack gap="xs">
      <Group justify="space-between">
        <Group gap="xs">
          <Text size="sm" fw={500}>
            Direction:
          </Text>
          <SegmentedControl
            size="xs"
            value={direction}
            onChange={(value) => setDirection(value as 'ltr' | 'rtl')}
            data={[
              { label: 'LTR', value: 'ltr' },
              { label: 'RTL', value: 'rtl' },
            ]}
          />
        </Group>
        <Button size="xs" variant="light" onClick={resetColumnsPinning}>
          Reset pinning
        </Button>
      </Group>
      <DirectionProvider key={`interactive-pinning-${direction}`} initialDirection={direction} detectDirection={false}>
        <Box dir={direction}>
          <DataTable withTableBorder storeColumnsKey={key} columns={effectiveColumns} records={records} />
        </Box>
      </DirectionProvider>
    </Stack>
  );
}

Combining pinning with column toggling

You can combine pinnable with toggleable on the same columns. This allows users to both pin/unpin columns and show/hide them. Right-click on the table header to open the column visibility menu.

In this example, some columns are hidden by default using defaultToggle: false. Use the resetColumnsToggle and resetColumnsPinning functions to reset visibility and pinning independently.

JeraldHowellJerald.Howell32@yahoo.com1950-05-21T10:19:10.565ZIndustrialRunte IncMcAllenMA2996 Ronny MountEngage synergistic infrastructures.
KathleenRueckerKathleen_Ruecker@hotmail.com1943-12-19T19:01:15.444ZComputersShanahan, Robel and BeierWest GerryKS378 Berta CrescentSynthesize customized portals.
EricaVolkmanErica.Volkman37@gmail.com1955-01-29T02:23:13.693ZToysGoyette IncDorthysideID8873 Mertz RapidProductize front-end web services.
CliffordOberbrunnerClifford.Oberbrunner@hotmail.com1979-03-06T02:19:39.619ZAutomotiveRau - O'HaraShieldsboroughMI7508 Lansdowne RoadInnovate real-time applications.
AlisonKlingAlison16@gmail.com1997-01-27T11:32:49.831ZJeweleryGoyette IncDorthysideID8873 Mertz RapidProductize front-end web services.
No records

Here is the code:

export function PinningAndTogglingExample() {
  const key = 'pinning-and-toggling-example';

  const { effectiveColumns, resetColumnsPinning, resetColumnsToggle } = useDataTableColumns<Employee>({
    key,
    columns: [
      { accessor: 'firstName', noWrap: true, pinnable: true, toggleable: true, pinned: 'left' },
      { accessor: 'lastName', noWrap: true, pinnable: true, toggleable: true },
      { accessor: 'email', noWrap: true, pinnable: true, toggleable: true },
      { accessor: 'birthDate', title: 'Birth date', pinnable: true, toggleable: true },
      { accessor: 'department.name', title: 'Department', pinnable: true, toggleable: true },
      { accessor: 'department.company.name', title: 'Company', noWrap: true, pinnable: true, toggleable: true },
      { accessor: 'department.company.city', title: 'City', noWrap: true, pinnable: true, toggleable: true },
      { accessor: 'department.company.state', title: 'State', pinnable: true, toggleable: true },
      {
        accessor: 'department.company.streetAddress',
        title: 'Address',
        noWrap: true,
        pinnable: true,
        toggleable: true,
      },
      {
        accessor: 'department.company.missionStatement',
        title: 'Mission statement',
        noWrap: true,
        width: 300,
        pinnable: true,
        toggleable: true,
      },
    ],
  });

  return (
    <>
      <DataTable withTableBorder storeColumnsKey={key} columns={effectiveColumns} records={records} />
      <Group justify="right" mt="xs">
        <Button size="xs" variant="light" onClick={resetColumnsToggle}>
          Reset visibility
        </Button>
        <Button size="xs" variant="light" onClick={resetColumnsPinning}>
          Reset pinning
        </Button>
      </Group>
    </>
  );
}

Pinning with column groups

When using column groups with interactive pinning, you can pin individual columns within groups. The group header will be pinned only when all columns in the group are pinned to the same side. If columns within a group have different pinning states (some pinned left, some unpinned, or mixed), the group header will not be pinned.

Try pinning and unpinning columns in the example below to see how the group headers behave:

EmployeeWorkplace
First name
Last name
Department
Company
City
State
Address
Mission statement
JeraldHowellIndustrialRunte IncMcAllenMA2996 Ronny MountEngage synergistic infrastructures.
KathleenRueckerComputersShanahan, Robel and BeierWest GerryKS378 Berta CrescentSynthesize customized portals.
EricaVolkmanToysGoyette IncDorthysideID8873 Mertz RapidProductize front-end web services.
CliffordOberbrunnerAutomotiveRau - O'HaraShieldsboroughMI7508 Lansdowne RoadInnovate real-time applications.
AlisonKlingJeweleryGoyette IncDorthysideID8873 Mertz RapidProductize front-end web services.
No records

Here is the code:

export function PinningWithColumnGroupsExample() {
  const key = 'pinning-with-column-groups-example';

  const { effectiveColumns, resetColumnsPinning } = useDataTableColumns<Employee>({
    key,
    columns: [
      { accessor: 'firstName', noWrap: true, pinnable: true, pinned: 'left' },
      { accessor: 'lastName', noWrap: true, pinnable: true, pinned: 'left' },
      { accessor: 'department.name', title: 'Department', pinnable: true },
      { accessor: 'department.company.name', title: 'Company', noWrap: true, pinnable: true },
      { accessor: 'department.company.city', title: 'City', noWrap: true, pinnable: true },
      { accessor: 'department.company.state', title: 'State', pinnable: true },
      { accessor: 'department.company.streetAddress', title: 'Address', noWrap: true, pinnable: true },
      { accessor: 'department.company.missionStatement', title: 'Mission statement', noWrap: true, pinnable: true },
    ],
  });

  // Build groups from effectiveColumns, preserving pinning state
  const employeeColumns = effectiveColumns.filter((c) => c.accessor === 'firstName' || c.accessor === 'lastName');
  const workplaceColumns = effectiveColumns.filter(
    (c) =>
      c.accessor === 'department.name' ||
      c.accessor === 'department.company.name' ||
      c.accessor === 'department.company.city' ||
      c.accessor === 'department.company.state' ||
      c.accessor === 'department.company.streetAddress' ||
      c.accessor === 'department.company.missionStatement'
  );

  return (
    <Stack gap="xs">
      <DataTable
        withTableBorder
        withColumnBorders
        storeColumnsKey={key}
        groups={[
          { id: 'employee', title: 'Employee', columns: employeeColumns },
          { id: 'workplace', title: 'Workplace', columns: workplaceColumns },
        ]}
        records={records}
      />
      <Group justify="right">
        <Button size="xs" variant="light" onClick={resetColumnsPinning}>
          Reset pinning
        </Button>
      </Group>
    </Stack>
  );
}