Examples › Column dragging and toggling

Starting with v7.3, Mantine DataTable supports column toggling and drag-and-drop reordering, thanks to the outstanding work of Giovambattista Fazioli.

Column drag-and-drop reordering

Name
Street address
City
State
Feest, Bogan and Herzog21716 Ratke DriveStromanportWY
Cummerata - Kuhlman6389 Dicki StreamSouth GateNH
Goyette Inc8873 Mertz RapidDorthysideID
Runte Inc2996 Ronny MountMcAllenMA
Goldner, Rohan and Lehner632 Broadway AvenueNorth LouieWY
Doyle, Hodkiewicz and O'Connell576 Joyce WaysTyraburghKS
Rau - O'Hara7508 Lansdowne RoadShieldsboroughMI
Tillman - Jacobi57918 Gwendolyn CirclesSheridanportMI
Connelly, Feest and Hodkiewicz7057 Stanley RoadKearaburghCA
Shanahan, Robel and Beier378 Berta CrescentWest GerryKS
Kling - McLaughlin8346 Kertzmann SquareSouth JoesphID
Jogi - McLaughlin83462 Shazam StreetNorth JoesphID
Jogi - McLaughlin83462 Shazam StreetNorth JoesphID
No records

In order to enable column dragging you’ll have to:

  • add a storeColumnsKey: 'your_key' property to the DataTable (since the order of the columns is persisted in the local storage);
  • add a draggable: true property to each dragging candidate column;
  • use the useDataTableColumns() hook to get the sorted columns.
'use client';

import { Button, Group, Stack } from '@mantine/core';
import { DataTable, useDataTableColumns } from 'mantine-datatable';
import { companies, type Company } from '~/data';

export default function DraggingExample() {
  const key = 'draggable-example';

  const { effectiveColumns, resetColumnsOrder } = useDataTableColumns<Company>({
    key,
    columns: [
      { accessor: 'name', width: '40%', draggable: true },
      { accessor: 'streetAddress', width: '60%', draggable: true },
      { accessor: 'city', width: 160, draggable: true },
      { accessor: 'state', textAlign: 'right' },
    ],
  });

  return (
    <Stack>
      <DataTable
        withTableBorder
        withColumnBorders
        storeColumnsKey={key}
        records={companies}
        columns={effectiveColumns}
      />
      <Group justify="right">
        <Button onClick={resetColumnsOrder}>Reset Column Order</Button>
      </Group>
    </Stack>
  );
}

Column toggling

In the example below:

  • you can toggle the first 3 columns;
  • the last column is not toggleable and will always be visible;
  • the first column is toggled off by default.

Right-click on the header to select the columns you want to toggle.

21716 Ratke DriveStromanportWY
6389 Dicki StreamSouth GateNH
8873 Mertz RapidDorthysideID
2996 Ronny MountMcAllenMA
632 Broadway AvenueNorth LouieWY
576 Joyce WaysTyraburghKS
7508 Lansdowne RoadShieldsboroughMI
57918 Gwendolyn CirclesSheridanportMI
7057 Stanley RoadKearaburghCA
378 Berta CrescentWest GerryKS
8346 Kertzmann SquareSouth JoesphID
83462 Shazam StreetNorth JoesphID
83462 Shazam StreetNorth JoesphID
No records

In order to enable column toggling you’ll have to:

  • add a storeColumnsKey: 'your_key' property to the DataTable (since the order of the columns is persisted in the local storage);
  • add a toggleable: true property to each toggling candidate column;
  • use the useDataTableColumns() hook to get the sorted columns.
'use client';

import { Button, Group, Stack, Text } from '@mantine/core';
import { IconBuildingCommunity, IconBuildingSkyscraper, IconMap, IconRoadSign } from '@tabler/icons-react';
import { DataTable, useDataTableColumns } from 'mantine-datatable';
import { companies } from '~/data';

export default function TogglingExample() {
  const key = 'toggleable-example';

  const { effectiveColumns, resetColumnsToggle } = useDataTableColumns({
    key,
    columns: [
      {
        accessor: 'name',
        title: (
          <Group gap={4} mt={-1}>
            <IconBuildingSkyscraper size={16} />
            <Text inherit mt={1}>
              Company
            </Text>
          </Group>
        ),
        width: '40%',
        toggleable: true,
        defaultToggle: false,
      },
      {
        accessor: 'streetAddress',
        title: (
          <Group gap={4} mt={-1}>
            <IconRoadSign size={16} />
            <Text inherit mt={1}>
              Street Address
            </Text>
          </Group>
        ),
        width: '60%',
        toggleable: true,
      },
      {
        accessor: 'city',
        title: (
          <Group gap={4} mt={-1}>
            <IconBuildingCommunity size={16} />
            <Text inherit mt={1}>
              City
            </Text>
          </Group>
        ),
        width: 160,
        toggleable: true,
      },
      {
        accessor: 'state',
        textAlign: 'right',
        title: (
          <Group justify="right">
            <IconMap size={16} />
          </Group>
        ),
      },
    ],
  });

  return (
    <Stack>
      <DataTable
        withTableBorder
        withColumnBorders
        storeColumnsKey={key}
        records={companies}
        columns={effectiveColumns}
      />
      <Group justify="right">
        <Button onClick={resetColumnsToggle}>Reset toggled columns</Button>
      </Group>
    </Stack>
  );
}

Add & Remove column at run-time

Of course, you may need to add or remove columns at run-time. In this case, you can directly modify the array of columns without needing to perform any operations.

21716 Ratke DriveStromanportWY
6389 Dicki StreamSouth GateNH
8873 Mertz RapidDorthysideID
2996 Ronny MountMcAllenMA
632 Broadway AvenueNorth LouieWY
576 Joyce WaysTyraburghKS
7508 Lansdowne RoadShieldsboroughMI
57918 Gwendolyn CirclesSheridanportMI
7057 Stanley RoadKearaburghCA
378 Berta CrescentWest GerryKS
8346 Kertzmann SquareSouth JoesphID
83462 Shazam StreetNorth JoesphID
83462 Shazam StreetNorth JoesphID
No records
'use client';

import { Button, Group, Stack, Text } from '@mantine/core';
import { IconBuildingCommunity, IconBuildingSkyscraper, IconMap, IconRoadSign } from '@tabler/icons-react';
import { DataTable, DataTableColumn, useDataTableColumns } from 'mantine-datatable';
import { useState } from 'react';
import { companies } from '~/data';

export default function DynamicColumnExample() {
  const key = 'dynamic-column-example';

  const [columns, setColumns] = useState<DataTableColumn[]>([
    {
      accessor: 'name',
      title: (
        <Group gap={4} mt={-1}>
          <IconBuildingSkyscraper size={16} />
          <Text inherit mt={1}>
            Company
          </Text>
        </Group>
      ),
      width: '40%',
      toggleable: true,
      defaultToggle: false,
    },
    {
      accessor: 'streetAddress',
      title: (
        <Group gap={4} mt={-1}>
          <IconRoadSign size={16} />
          <Text inherit mt={1}>
            Street Address
          </Text>
        </Group>
      ),
      width: '60%',
      toggleable: true,
    },
    {
      accessor: 'city',
      title: (
        <Group gap={4} mt={-1}>
          <IconBuildingCommunity size={16} />
          <Text inherit mt={1}>
            City
          </Text>
        </Group>
      ),
      width: 160,
      toggleable: true,
    },
    {
      accessor: 'state',
      textAlign: 'right',
      title: (
        <Group justify="right">
          <IconMap size={16} />
        </Group>
      ),
    },
  ]);

  const { effectiveColumns, resetColumnsToggle } = useDataTableColumns({
    key,
    columns,
  });

  // add or remove the whole record with missionStatement accessor
  function toggleColumnMissionStatement() {
    const newColumns = columns.filter((col) => col.accessor !== 'missionStatement');
    if (columns.length === newColumns.length) {
      newColumns.push({
        accessor: 'missionStatement',
        title: (
          <Group gap={4} mt={-1} wrap="nowrap">
            <IconBuildingSkyscraper size={16} />
            <Text inherit mt={1}>
              Mission Statement
            </Text>
          </Group>
        ),
        width: '40%',
        toggleable: true,
        defaultToggle: true,
      });
    }
    setColumns(newColumns);
  }

  return (
    <Stack>
      <Group>
        <Button onClick={toggleColumnMissionStatement}>Toggle Mission Statement column</Button>
      </Group>
      <DataTable
        withTableBorder
        withColumnBorders
        storeColumnsKey={key}
        records={companies}
        columns={effectiveColumns}
      />
      <Group justify="right">
        <Button onClick={resetColumnsToggle}>Reset toggled columns</Button>
      </Group>
    </Stack>
  );
}

Dragging and toggling with context menu reset

Feest, Bogan and Herzog21716 Ratke DriveStromanportWY
Cummerata - Kuhlman6389 Dicki StreamSouth GateNH
Goyette Inc8873 Mertz RapidDorthysideID
Runte Inc2996 Ronny MountMcAllenMA
Goldner, Rohan and Lehner632 Broadway AvenueNorth LouieWY
Doyle, Hodkiewicz and O'Connell576 Joyce WaysTyraburghKS
Rau - O'Hara7508 Lansdowne RoadShieldsboroughMI
Tillman - Jacobi57918 Gwendolyn CirclesSheridanportMI
Connelly, Feest and Hodkiewicz7057 Stanley RoadKearaburghCA
Shanahan, Robel and Beier378 Berta CrescentWest GerryKS
Kling - McLaughlin8346 Kertzmann SquareSouth JoesphID
Jogi - McLaughlin83462 Shazam StreetNorth JoesphID
Jogi - McLaughlin83462 Shazam StreetNorth JoesphID
No records
'use client';

import { IconColumnRemove, IconColumns3 } from '@tabler/icons-react';
import { DataTable, useDataTableColumns } from 'mantine-datatable';
import { useContextMenu } from 'mantine-contextmenu';
import { companies } from '~/data';

export default function DraggingTogglingResetExample() {
  const { showContextMenu } = useContextMenu();

  const key = 'toggleable-reset-example';

  const { effectiveColumns, resetColumnsOrder, resetColumnsToggle } = useDataTableColumns({
    key,
    columns: [
      { accessor: 'name', width: '40%', toggleable: true, draggable: true },
      { accessor: 'streetAddress', width: '60%', toggleable: true, draggable: true },
      { accessor: 'city', width: 160, toggleable: true, draggable: true },
      { accessor: 'state', textAlign: 'right' },
    ],
  });

  return (
    <DataTable
      withTableBorder
      withColumnBorders
      storeColumnsKey={key}
      records={companies}
      columns={effectiveColumns}
      onRowContextMenu={({ event }) =>
        showContextMenu([
          {
            key: 'reset-toggled-columns',
            icon: <IconColumnRemove size={16} />,
            onClick: resetColumnsToggle,
          },
          {
            key: 'reset-columns-order',
            icon: <IconColumns3 size={16} />,
            onClick: resetColumnsOrder,
          },
        ])(event)
      }
    />
  );
}

Complex usage

Connelly, Feest and Hodkiewicz7057 Stanley RoadKearaburghCA
Cummerata - Kuhlman6389 Dicki StreamSouth GateNH
Doyle, Hodkiewicz and O'Connell576 Joyce WaysTyraburghKS
Feest, Bogan and Herzog21716 Ratke DriveStromanportWY
Goldner, Rohan and Lehner632 Broadway AvenueNorth LouieWY
Goyette Inc8873 Mertz RapidDorthysideID
Jogi - McLaughlin83462 Shazam StreetNorth JoesphID
Jogi - McLaughlin83462 Shazam StreetNorth JoesphID
Kling - McLaughlin8346 Kertzmann SquareSouth JoesphID
Rau - O'Hara7508 Lansdowne RoadShieldsboroughMI
Runte Inc2996 Ronny MountMcAllenMA
Shanahan, Robel and Beier378 Berta CrescentWest GerryKS
Tillman - Jacobi57918 Gwendolyn CirclesSheridanportMI
No records
'use client';

import { IconColumnRemove, IconColumns3 } from '@tabler/icons-react';
import { DataTable, useDataTableColumns, type DataTableSortStatus } from 'mantine-datatable';
import sortBy from 'lodash/sortBy';
import { useContextMenu } from 'mantine-contextmenu';
import { useEffect, useState } from 'react';
import { companies, type Company } from '~/data';

export default function DraggingTogglingComplexExample() {
  const { showContextMenu } = useContextMenu();

  const [sortStatus, setSortStatus] = useState<DataTableSortStatus<Company>>({
    columnAccessor: 'name',
    direction: 'asc',
  });

  const [records, setRecords] = useState(sortBy(companies, 'name'));

  useEffect(() => {
    const data = sortBy(companies, sortStatus.columnAccessor) as Company[];
    setRecords(sortStatus.direction === 'desc' ? data.reverse() : data);
  }, [sortStatus]);

  const key = 'toggleable-reset-example';

  const { effectiveColumns, resetColumnsOrder, resetColumnsToggle } = useDataTableColumns({
    key,
    columns: [
      { accessor: 'name', width: '40%', toggleable: true, draggable: true, sortable: true },
      { accessor: 'streetAddress', width: '60%', toggleable: true, draggable: true },
      { accessor: 'city', width: 160, toggleable: true, draggable: true },
      { accessor: 'state', textAlign: 'right' },
    ],
  });

  return (
    <DataTable
      withTableBorder
      withColumnBorders
      storeColumnsKey={key}
      records={records}
      columns={effectiveColumns}
      sortStatus={sortStatus}
      onSortStatusChange={setSortStatus}
      onRowContextMenu={({ event }) =>
        showContextMenu([
          {
            key: 'reset-toggled-columns',
            icon: <IconColumnRemove size={16} />,
            onClick: resetColumnsToggle,
          },
          {
            key: 'reset-columns-order',
            icon: <IconColumns3 size={16} />,
            onClick: resetColumnsOrder,
          },
        ])(event)
      }
    />
  );
}