Row Reordering Pro
The contents of this page concern the Pro version of react-bootstrap-data-grid. Using the Pro version in production requires a commercial license, which is not yet available.
A commercial license for the Pro version is expected to be available around July 1, 2026. In the meantime (or even after release), feel free to use the Pro version for evaluation purposes without a license.
The Pro version of react-bootstrap-data-grid allows a developer to allow the user to rearrange rows in a grid through a drag-and-drop interface.
End User Usage
When reordering is enabled for a grid, there will be an extra column on the left side of the grid that contains buttons that are drag handles. One can drag the handle to a different position on the grid to move the row to that position.
Drop Zones
The drop zones are around the borders between rows and extend half of the height of each adjacent row. See the image below for an illustration of the location of the drag handles and the drop zone that corresponds to a particular row index.
Visual Indication
When the user drags a reorder handle, the styling of the top and bottom borders of the row being dragged changes to indicate that fact. By default, the color of the borders change to the Bootstrap success color.
In addition, when the user hovers over a drop zone while dragging, the border between cells indicating where the dropped row will be inserted will undergo a style change. By default, the Bootstrap primary color is used.
The below illustration shows the location of highlighted borders as described above.
Drag Marker
When the user is dragging a row, a translucent div appears under the cursor that contains the ID of the row. A common term for this div is drag ghost.
Sorting and Filtering
When a table is being sorted or filtered, the reordering feature is also disabled by disabling the buttons that are the drag handles.
Note that even if sorting or filtering is enabled for a grid, by passing in a sortModel or filterModel prop,
respectively, those features may not be active. Sorting is not active if no column is being sorted upon. Likewise,
if filtering id disabled for all columns, filtering is not active.
The reorder drag handle buttons are disabled if either sorting or filtering is active.
Keyboard Navigation
To reorder rows with the keyboard:
- Use the Tab key to cycle through focus on elements on the page until you get to the resize handle of the row that you want to reorder. Those buttons have a horizontal grip icon: .
- Click on the button using a keyboard key. For most browsers, either Space or Enter can be used to click on a button once it is focused. Note that the button behaves differently when clicking with a pointing device or touch screen (i.e. nothing happens).
- You should see the same visual indications as with a pointing device for the selected row (Bootstrap
success-colored borders by default) and the destination index (Bootstrapprimary-colored border by default). - Used the Up Arrow and Down Arrow keys to select the index to which you want to move the selected row.
- Press Enter or Space key to perform the reorder to the selected destination index. To cancel the reordering process at any time without reordering anything, press the Escape key.
Enabling
To enable the feature for a grid, pass in a reorderModel prop. This prop is an object with a single member named
callback, which is a callback function of the type (id: RowId, destIndex: number) => void;.
This callback function takes the row id and destIndex, which is the destination index from the drag and drop action.
It is up to this function to act upon the action, often by setting a new rows prop with
the rows in a different order.
Row Insert Utility Function
A function that takes an array of rows, a RowId, and a destination index and swaps the position of the element is
often useful for this callback function. It is not trivial to write and thus has been exported as a utility function
reorderRows.
To use the utility function, import it from the react-bootstrap-data-grid pro package as follows:
import { reorderRows } from "@absreim/react-bootstrap-data-grid-pro";One imported, a typical way to use the function to implement a reorder callback is as follows
const initRows = [
// initial rows...
]
const ReorderableGrid: FC = () => {
const [rows, setRows] = useState(initRows);
const reorderCallback: ReorderCallback = useCallback(
(id, destIndex) => {
const newRows = reorderRows(rows, id, destIndex);
setRows(newRows);
},
[rows],
);
return (
<GridPro
rows={rows}
cols={cols}
reorder={{ callback: reorderCallback }}
/>
);
}Caveats
This feature currently does not support the situation where the contents of the rows prop changes while the user is dragging it. In such a situation, the behavior of this feature may be difficult to predict.
If you expect that your app would cause the rows prop to update via means other than user input, it is recommended that you not enable this feature.
Example
The following example demonstrates a grid with row reordering enabled.
Sorting and Filtering
In addition, sorting and filtering are enabled. One can sort on a column and/or filter on one or more columns to see how row reordering becomes disabled.
To enable sorting, click on the header cell for a sortable column to toggle through sorting modes. For this example, all columns except Description are sortable.
To enable filtering, click on the Filtering button in the toolbar (icon is ), enable filtering for at least one column, and click the Submit button.
Custom Styles
This example demonstrates custom styling specific to the row reordering feature:
- The border color of dragged row is changed to the Bootstrap
warningcolor. - The border color of drop zone is changed ot the Bootstrap
infocolor. - The drag ghost has reduced opacity and a thicker border.
Live Demo
Name | HP | Speed (m/s) | Attack | Range (m) | Description | |
|---|---|---|---|---|---|---|
| Fire Badger | 4222 | 9 | 27 | 75 | Medium anti-swarm tank | |
| Hacker | 3249 | 8 | 585 | 110 | Hacker bot | |
| Mustang | 343 | 16 | 36 | 95 | High-speed all-purpose tank | |
| Phantom Ray | 3412 | 16 | 1087 | 65 | Medium-sized aircraft | |
| Phoenix | 1491 | 16 | 3219 | 120 | Long-range strike aircraft | |
| Rhino | 19297 | 16 | 3560 | 6 | Heavy melee bot | |
| Sabertooth | 15541 | 8 | 7858 | 95 | Heavy tank | |
| Sledgehammer | 3478 | 7 | 608 | 95 | Heavy tank | |
| Steel Ball | 4571 | 16 | 55 | 45 | High-speed anti-unit bot | |
| Stormcaller | 1149 | 6 | 772 | 180 | Heavy rocket launcher | |
| Tarantula | 14773 | 8 | 496 | 80 | Heavy mech | |
| Wasp | 311 | 16 | 202 | 50 | Light aircraft |
Code
index.tsx
"use client";
import GridPro, {
ProColDef,
reorderRows,
RowDef,
ReorderCallback,
ReorderStyleModel,
} from "@absreim/react-bootstrap-data-grid-pro";
import { UnitStats } from "@/assets/shared/types";
import { FC, useCallback, useState } from "react";
import "./custom.scss";
const initRows: RowDef<UnitStats>[] = [
{
id: "Fire Badger",
data: {
name: "Fire Badger",
hp: 4222,
speed: 9,
attack: 27,
range: 75,
desc: "Medium anti-swarm tank",
},
},
{
id: "Hacker",
data: {
name: "Hacker",
hp: 3249,
speed: 8,
attack: 585,
range: 110,
desc: "Hacker bot",
},
},
{
id: "Mustang",
data: {
name: "Mustang",
hp: 343,
speed: 16,
attack: 36,
range: 95,
desc: "High-speed all-purpose tank",
},
},
{
id: "Phantom Ray",
data: {
name: "Phantom Ray",
hp: 3412,
speed: 16,
attack: 1087,
range: 65,
desc: "Medium-sized aircraft",
},
},
{
id: "Phoenix",
data: {
name: "Phoenix",
hp: 1491,
speed: 16,
attack: 3219,
range: 120,
desc: "Long-range strike aircraft",
},
},
{
id: "Rhino",
data: {
name: "Rhino",
hp: 19297,
speed: 16,
attack: 3560,
range: 6,
desc: "Heavy melee bot",
},
},
{
id: "Sabertooth",
data: {
name: "Sabertooth",
hp: 15541,
speed: 8,
attack: 7858,
range: 95,
desc: "Heavy tank",
},
},
{
id: "Sledgehammer",
data: {
name: "Sledgehammer",
hp: 3478,
speed: 7,
attack: 608,
range: 95,
desc: "Heavy tank",
},
},
{
id: "Steel Ball",
data: {
name: "Steel Ball",
hp: 4571,
speed: 16,
attack: 55,
range: 45,
desc: "High-speed anti-unit bot",
},
},
{
id: "Stormcaller",
data: {
name: "Stormcaller",
hp: 1149,
speed: 6,
attack: 772,
range: 180,
desc: "Heavy rocket launcher",
},
},
{
id: "Tarantula",
data: {
name: "Tarantula",
hp: 14773,
speed: 8,
attack: 496,
range: 80,
desc: "Heavy mech",
},
},
{
id: "Wasp",
data: {
name: "Wasp",
hp: 311,
speed: 16,
attack: 202,
range: 50,
desc: "Light aircraft",
},
},
];
const cols: ProColDef[] = [
{
name: "name",
type: "string",
label: "Name",
sortable: true,
},
{
name: "hp",
type: "number",
label: "HP",
sortable: true,
},
{
name: "speed",
type: "number",
label: "Speed (m/s)",
sortable: true,
},
{
name: "attack",
type: "number",
label: "Attack",
sortable: true,
},
{
name: "range",
type: "number",
label: "Range (m)",
sortable: true,
},
{
name: "desc",
type: "string",
label: "Description",
},
];
const reorderStyleModel: ReorderStyleModel = {
draggedRowClasses: ["custom-reorder-dragged-row"],
draggedRowPredecessorClasses: ["custom-reorder-dragged-row-pred"],
topBorderRowClasses: ["custom-reorder-above-drag-target-row"],
bottomBorderRowClasses: ["custom-reorder-below-drag-target-row"],
ghostDivClasses: ["custom-reorder-ghost", "border", "border-3"],
};
const ReorderableGrid: FC = () => {
const [rows, setRows] = useState(initRows);
const reorderCallback: ReorderCallback = useCallback(
(id, destIndex) => {
const newRows = reorderRows(rows, id, destIndex);
setRows(newRows);
},
[rows],
);
return (
<GridPro
useToolbar
rows={rows}
cols={cols}
sortModel={{
type: "uncontrolled",
initialSortColDef: null,
}}
filterModel={{ type: "uncontrolled" }}
reorder={{ callback: reorderCallback }}
styleModel={{
reorderModel: reorderStyleModel,
}}
/>
);
};
export default ReorderableGrid;custom.scss
.custom-reorder-ghost {
opacity: 50%;
font-weight: bold;
position: fixed;
padding: 0.5rem;
z-index: 2;
}
.custom-reorder-dragged-row-pred {
border-bottom-color: var(--bs-warning);
}
.custom-reorder-dragged-row {
border-top-width: 1px;
opacity: 50%;
border-color: var(--bs-warning);
}
.custom-reorder-above-drag-target-row {
border-bottom-color: var(--bs-info);
}
.custom-reorder-below-drag-target-row {
border-top-width: 1px;
border-top-color: var(--bs-info);
}types.ts
export interface UnitStats {
name: string;
hp: number;
speed: number;
attack: number;
range: number;
desc: string;
}Style Customization
Styles related to the row reordering feature can customized by supplying a property of the styleModel prop of the
GridPro component. This property is named reorderModel and of the type named ReorderStyleModel. Properties of
ReorderStyleModel are listed below.
| Property name | Type definition | Required/Optional | Description |
|---|---|---|---|
| draggedRowClasses | string[] | Optional | Classes to apply to the Defaults to |
| draggedRowPredecessorClasses | string[] | Optional | Classes to apply to the Defaults to |
| topBorderRowClasses | string[] | Optional | Classes to apply to the Defaults to |
| bottomBorderRowClasses | string[] | Optional | Classes to apply to the Defaults to |
| ghostDivClasses | string[] | Optional | Classes to apply to the drag ghost Defaults to
|