Context menu
Set contextMenu to give the table a built-in context menu. Header cells get a menu of built-in actions — sort ascending/descending, clear sort, pin left/right, hide column, and reset — plus any custom items you add. Rows get a menu of whatever actions you supply via contextMenuActions.row (edit, delete, duplicate, and so on); with no actions supplied, rows have no menu. The menu opens on right-click, from a menu (⋮) button, or both.
Header menu
Right-click a header, or use its ⋮ button, to sort, pin left/right, hide, or reset columns — all built in, no config beyond contextMenu. The table is wider than its container, so pin a column then scroll horizontally to see it stick. The row menu is turned off here (row: false); it gets its own section below.
Last action: none yet — right-click a header or use a ⋮ button · Right-click a header, or use its ⋮ button, to sort, pin, hide, or reset columns.
import DataTable from 'react-data-table-component';
<DataTable
columns={columns}
data={data}
// Header menu only — right-click a header or use its ⋮ button
contextMenu={{ row: false, trigger: 'both' }}
onContextMenuAction={(action, ctx) => {
if (ctx.type === 'header') console.log(action.id, ctx.column);
}}
resizable
/>;Enabling the menu
contextMenu accepts true for the defaults (header and row menus, right-click trigger) or a config object. The trigger controls how the menu opens:
'right-click'(default) — right-click a header cell or a row to open its menu.'menu-button'— show a ⋮ menu button instead; the best choice for touch-first UIs, where right-click isn't available. Row menu buttons appear on hover and are always visible on touch devices.'both'— right-click and a ⋮ menu button.
// Everything on, right-click only (default trigger)
<DataTable contextMenu />
// Menu buttons only — the right choice for touch-first UIs
<DataTable contextMenu={{ trigger: 'menu-button' }} />
// Both triggers, header menu only
<DataTable contextMenu={{ row: false, trigger: 'both' }} />
// Row menu button on the left edge instead of the right (RTL-aware: 'start' | 'end')
<DataTable contextMenu={{ trigger: 'both', menuPosition: 'start' }} />Row menu
contextMenu on its own gives you the header menu. The row menu hasno built-in actions, so it stays off until you supply them viacontextMenuActions.row — a function that returns the menu items for a given row. It receives the row and its index, so you can vary or disable items per row. A row that returns an empty array (or no row function at all) has no menu, and right-clicking it falls through to the browser's native menu.
Row menu
Right-click a row or use its ⋮ button. The items come from contextMenuActions.row and mutate the data — mark a task done, duplicate it, or delete it. 'Mark done' disables itself once a task is already Done, showing per-row item control. Use the position select to move the ⋮ button between the row's end (default) and start edge.
Last action: none yet — open the ⋮ menu on any row · Right-click a row or use its ⋮ button.
import DataTable, { type ContextMenuAction, type TableColumn } from 'react-data-table-component';
// Row items are a function of the row, so they can vary or disable per row.
const rowActions = (row: Task): ContextMenuAction[] => [
{ id: 'mark-done', label: 'Mark done', disabled: row.status === 'Done' },
{ id: 'duplicate', label: 'Duplicate' },
{ id: 'delete', label: 'Delete' },
];
<DataTable
columns={columns}
data={rows}
// 1. Turn the feature on (right-click, menu-button, or both).
// menuPosition moves the row ⋮ button: 'end' (default) or 'start'.
contextMenu={{ trigger: 'both', menuPosition: 'end' }}
// 2. Supply the row menu items — without this, rows have no menu
contextMenuActions={{ row: rowActions }}
// 3. Handle the selection
onContextMenuAction={(action, ctx) => {
if (ctx.type !== 'row') return;
if (action.id === 'delete') setRows(prev => prev.filter(r => r.id !== ctx.row.id));
if (action.id === 'mark-done') setRows(prev => prev.map(r => (r.id === ctx.row.id ? { ...r, status: 'Done' } : r)));
}}
/>;Built-in header actions
- Sort ascending / descending / clear sort — shown for sortable columns. Explicit direction, no cycling.
- Pin left / Pin right / Unpin — wires to the same mechanism as column pinning; the column moves into the pinned band. Pinning is only visible when the table scrolls horizontally, so give columns explicit widths (see columns need widths). Hidden when column groups are used, since pinning would tear a column out of its group.
- Hide column — removes the column from view. Disabled when it is the last visible column.
- Reset table — restores the original column order, visibility, and pin state, clears the sort, and clears all column filters.
Custom actions
Header items are appended after the built-ins, as a static list or a function of the column. Row items are always a function of the row. Selections arrive inonContextMenuAction — built-ins fire it too, after their effect is applied, so you can persist state changes (for example the new pin layout viaonColumnOrderChange).
<DataTable
contextMenu
contextMenuActions={{
header: column => [{ id: 'export-col', label: `Export ${column.name}` }],
row: row => [
{ id: 'edit', label: 'Edit', icon: <PencilIcon /> },
{ id: 'delete', label: 'Delete' },
],
}}
onContextMenuAction={(action, ctx) => {
if (ctx.type === 'header') console.log(action.id, ctx.column);
else console.log(action.id, ctx.row, ctx.rowIndex);
}}
/>;The built-in ids are reserved: sort-asc, sort-desc,clear-sort, pin-left, pin-right, unpin,hide-column, reset. Use different ids for custom actions.
Accessibility and behaviour
- The menu renders with
role="menu"/role="menuitem"; Arrow keys, Home, and End move focus, Escape closes and returns focus to the trigger. - Menu buttons carry
aria-haspopup="menu"and localizable labels; row menu buttons appear on hover/focus and are always visible on touch devices. - On filterable columns the header menu button renders after the filter icon, so it is always the outermost control — mirrored automatically in RTL.
- The menu flips above the pointer near the bottom edge and clamps within the viewport; it closes on outside click, scroll, and resize.
- In RTL the button-anchored menu aligns to the button's opposite edge automatically.
- All labels can be translated via
localization.contextMenu— see Localization. The bundled locales include translations.
Props
| Prop | Type | Description |
|---|---|---|
contextMenu | boolean | { header?: boolean; row?: boolean; trigger?: 'right-click' | 'menu-button' | 'both'; menuPosition?: 'start' | 'end' } | Enable the context menu. true enables header and row menus with the right-click trigger. The header menu has built-in sort/pin/hide/reset actions; the row menu only opens when contextMenuActions.row returns items. |
contextMenuActions | { header?: ContextMenuAction[] | (column) => ContextMenuAction[]; row?: (row, rowIndex) => ContextMenuAction[] } | Custom menu items. Header items are appended after the built-ins; row items are the entire row menu. |
onContextMenuAction | (action: ContextMenuAction, ctx: ContextMenuActionContext<T>) => void | Called for every selected item, built-ins included (after their effect is applied). ctx is { type: 'header', column } or { type: 'row', row, rowIndex }. |
ContextMenuAction
| Field | Type | Description |
|---|---|---|
| id | string | Identifier passed to onContextMenuAction. Built-in ids are reserved. |
| label | ReactNode | Item content. |
| disabled | boolean | Renders the item disabled. |
| icon | ReactNode | Optional icon rendered before the label. |