Skip to content

Column reordering

Drag a header sideways to put the column where you want it. The menu has Move left and Move right for the keyboard.

Zara Sharma$0.00InvitedFreeDesignMay 4, 2023
Kavya Müller$0.00PausedFreeDataNov 8, 2024
Olivia Müller$0.00InvitedFreeDesignNov 19, 2023
Omar Iyer$0.00PausedFreeSalesFeb 10, 2026
Elias Garcia$0.00ChurnedFreeFinanceDec 22, 2025
Chloe Rossi$1,276.00PausedTeamResearchSep 2, 2024
Sofia Garcia$48.00ActiveProDesignJan 11, 2026
Idris Bose$48.00InvitedProSupportMay 21, 2025
Noah Bose$0.00ActiveFreePlatformApr 20, 2024
Zara Sharma$1,595.00ActiveTeamResearchJul 19, 2026
0 of 50 row(s) selected.
Page 1 of 5

Press on a header’s title and move. The drag starts after 6 px, so a click still sorts. The columns rearrange under the pointer as you pass over them; release to keep the new order, or press Escape to put everything back where it was.

From the column menu (the ⋯ button at the right of the header, or right-click), Move left and Move right swap the column with its visible neighbour. They are disabled at the first and last position.

To start in an order of your own, list the ids:

<DataGrid
data={data}
columns={columns}
initialState={{ columnOrder: ["select", "name", "mrr", "status", "plan", "team", "joinedAt"] }}
/>

Ids you leave out follow the listed ones, in the order you defined the columns; ids that match no column are ignored. An empty array means definition order. From code:

grid.table.setColumnOrder(["name", "team", "mrr"])
grid.table.resetColumnOrder()
grid.table.state.columnOrder // [] until someone reorders
  • Pointer only, header only. Dragging uses dnd kit’s pointer sensor, so it works with a mouse, a pen or a finger, and the dragged header cannot leave the header row. The header being dragged fades to 80 % and the one it would land on is tinted. Hovering a draggable title shows the hint Drag to reorder column (labels.dragColumn).
  • The order is one list. Dragging and the menu both edit columnOrder, a list of every leaf column id. The screen order is that list, with pinned columns moved to their edges and grouped columns moved to the front.
  • Columns that don’t drag:
    • The utility columns, col.select(), col.expander(), col.rowNumber() and col.actions(), and any column with stable: true. They are rendered outside the drag machinery because a dragging header carries a CSS transform and re-registers with the sensor as the grid changes, which gives a menu anchored in the column an unstable position. They are neither draggable nor drop targets.
    • Pinned columns: no handle, and no Move items in the menu. Unpin to move.
    • Grouped columns: the grid’s groupedColumnMode is "reorder", which moves them to the front for as long as they are grouped; their handle is off meanwhile.
    • Group headers (col.group) don’t drag. Their leaf columns do.
    • A column with draggable: false has no handle but keeps its place in the list, so it still shifts when its neighbours are dragged past it.
  • Move left and Move right work on the centre region’s visible columns, utility columns included, so a column can be moved past the checkbox column from the menu even though it cannot be dragged there.
  • Hidden columns keep their place in the order and reappear there.
  • Persistence. columnOrder is part of the view the grid remembers with persist and of saved views. Reset view restores definition order. URL state does not carry it: order is a preference, not a query.
Key Does
Tab to a header, then its ⋯ button, Enter Opens the column menu with Move left and Move right.
Escape while dragging Cancels the drag and restores the order from before it.

Column options:

Option Type Default What it does
draggable boolean true false hides the drag handle; the column still moves when others are reordered.
stable boolean false (true on utility columns) Renders the column outside the drag machinery. Set it on any column whose cell anchors a menu or popover.

Grid options:

Option Type Default What it does
initialState.columnOrder string[] [] (definition order) The starting order, by id. A persisted view wins over it.
table.onColumnOrderChange + table.state.columnOrder Control the slice yourself.