In the previous part, we went through a detailed approach to creating an editable React table with a dynamic column schema. We also utilized TanStack Table to simplify the process. The code from the previous part is available here in Stackblitz.
In this part, we will continue building on what we’ve done previously and add the ability to remove and add table rows with multiple row selections.
Below is a short video of how we want the table to look and behave at the end of this tutorial:
As we continue building upon the same code from the previous part, it’s recommended that you go through the first tutorial to understand how the code structure works as we will not be explaining it here. You can navigate to any part using the table of contents.
Add table row
To enable adding table rows, we must add an “Add Row” button and then create the logic to insert an empty row into the data array.
Let’s start by adding the logic to insert the row in the main table component. Like the previous approach, we’ll attach the function in the meta
object in the useReactTableHook
. This way, we can access it anywhere through the main table
object:
const table = useReactTable({
data,
columns,
getCoreRowModel: getCoreRowModel(),
meta: {
editedRows,
setEditedRows,
revertData: (...) => {
...
},
updateData: (...) => {
...
},
addRow: () => {
const newRow: Student = {
studentId: Math.floor(Math.random() * 10000),
name: "",
dateOfBirth: "",
major: "",
};
const setFunc = (old: Student[]) => [...old, newRow];
setData(setFunc);
setOriginalData(setFunc);
},
},
});
Note that we also updated the originalData
array, as we need to keep them in sync for the cancel/save row functionality discussed in the first part.
Now, we can create a Footer Cell component with a button and event handler to consume the addRow
function.
export const FooterCell = ({ table }) => {
const meta = table.options.meta
return (
<div className="footer-buttons">
<button className="add-button" onClick={meta?.addRow}>
Add New +
</button>
</div>
)
}
Lastly, we need to display the FooterCell
component somewhere on the table for the user to consume. There are multiple options, but adding it to the footer’s main table can be a good place as it will feel more integrated.
import { FooterCell } from '../Table/FooterCell';
export const Table = () => {
const [data, setData] = useState(() => [...defaultData]);
const [originalData, setOriginalData] = useState(() => [...defaultData]);
const [editedRows, setEditedRows] = useState({});
const table = useReactTable({
data,
columns,
getCoreRowModel: getCoreRowModel(),
meta: {
editedRows,
setEditedRows,
revertData: (...) => {
...
},
updateData: (...) => {
...
},
addRow: () => {
const newRow: Student = {
studentId: Math.floor(Math.random() * 10000),
name: "",
dateOfBirth: "",
major: "",
};
const setFunc = (old: Student[]) => [...old, newRow];
setData(setFunc);
setOriginalData(setFunc);
},
},
});
return (
<article className="table-container">
<table>
<thead>
...
</thead>
<tbody>
...
</tbody>
<tfoot>
<tr>
<th colSpan={table.getCenterLeafColumns().length} align="right">
<FooterCell table={table} />
</th>
</tr>
</tfoot>
</table>
</article>
);
};
Below is a live example of the code above with some button styling:
table .footer-buttons button {
border: none;
background-color: transparent;
}
table .add-button {
color: #4bbd7f;
}
Remove table row
Removing table rows will follow the same approach as the previous section. Let’s start by adding the removeRow
function, a simple one-liner filter method to remove the row from the data and original data arrays.
const table = useReactTable({
data,
columns,
getCoreRowModel: getCoreRowModel(),
meta: {
editedRows,
setEditedRows,
revertData: (...) => {
...
},
updateData: (...) => {
...
},
addRow: () => {
...
},
removeRow: (rowIndex: number) => {
const setFilterFunc = (old: Student[]) =>
old.filter((_row: Student, index: number) => index !== rowIndex);
setData(setFilterFunc);
setOriginalData(setFilterFunc);
},
},
});
For the “Remove” button, creating a new component wouldn’t be necessary as it will reside next to the “Edit” button in the EditCell
component.
Now, we can add a removeRow
function in the EditCell
component and call it on the “Remove” button’s onClick
event.
export const EditCell = ({ row, table }) => {
const meta = table.options.meta;
const setEditedRows = (...) => {
...
};
const removeRow = () => {
meta?.removeRow(row.index);
};
return (
<div className="edit-cell-container">
{meta?.editedRows[row.id] ? (
<div className="edit-cell-action">
<button onClick={setEditedRows} name="cancel">
⚊
</button>{" "}
<button onClick={setEditedRows} name="done">
✔
</button>
</div>
) : (
<div className="edit-cell-action">
<button onClick={setEditedRows} name="edit">
✐
</button>
<button onClick={removeRow} name="remove">
X
</button>
</div>
)}
</div>
);
};
Note that we changed the “Cancel” icon to a minus sign (⚊) and the “Remove” icon to a cross (X) for better clarity.
Below is a live demo of the code above with some button styling and alignment:
table .edit-cell-action button[name="edit"] {
color: #ffb918;
}
table .edit-cell-action button[name="cancel"] {
color: #7b7b7b;
}
table .edit-cell-action button[name="done"] {
color: #4bbd7f;
}
table .edit-cell-action button[name="remove"] {
color: red;
background-color: rgb(230, 208, 208);
}
table .edit-cell-action {
display: flex;
gap: 5px;
}
Remove multiple table rows
It’s always helpful to select and remove multiple rows at once. Luckily, row selection is available out of the box from TanStack Table. All we need to do is enable the feature and pass in the proper methods; it will manage the rest for us.
First, we’ll add enableRowSelection
in useReactTable
and create a new removeSelectedRows
function. The function will receive an array of selected row IDs and filter them out from the data
array.
const table = useReactTable({
data,
columns,
getCoreRowModel: getCoreRowModel(),
enableRowSelection: true,
meta: {
editedRows,
setEditedRows,
revertData: (...) => {
...
},
updateData: (...) => {
...
},
addRow: () => {
...
},
removeRow: (...) => {
...
},
removeSelectedRows: (selectedRows: number[]) => {
const setFilterFunc = (old: Student[]) =>
old.filter((_row, index) => !selectedRows.includes(index));
setData(setFilterFunc);
setOriginalData(setFilterFunc);
},
},
});
Then, in the EditCell
component, we’ll add a checkbox input with the appropriate props to capture and update the selected rows. The event handlers are coming from the row
object available for us from the useReactTable
hook.
export const EditCell = ({ row, table }) => {
const meta = table.options.meta;
const setEditedRows = (...) => {
...
};
const removeRow = () => {
meta?.removeRow(row.index);
};
return (
<div className="edit-cell-container">
{meta?.editedRows[row.id] ? (
<div className="edit-cell-action">
<button onClick={setEditedRows} name="cancel">
⚊
</button>{" "}
<button onClick={setEditedRows} name="done">
✔
</button>
</div>
) : (
<div className="edit-cell-action">
<button onClick={setEditedRows} name="edit">
✐
</button>
<button onClick={removeRow} name="remove">
X
</button>
</div>
)}
<input
type="checkbox"
checked={row.getIsSelected()}
onChange={row.getToggleSelectedHandler()}
/>
</div>
);
};
Last, we’ll add a new Remove Selected
button to the FooterCell
component and pass in the removeSelectedRows
function from the meta
object. The selected rows can be retrieved from the getSelectedRowModel
function that has rows
property.
export const FooterCell = ({ table }) => {
const meta = table.options.meta
const selectedRows = table.getSelectedRowModel().rows
const removeRows = () => {
meta.removeSelectedRows(
table.getSelectedRowModel().rows.map(row => row.index)
)
table.resetRowSelection()
}
return (
<div className="footer-buttons">
{selectedRows.length > 0 ? (
<button className="remove-button" onClick={removeRows}>
Remove Selected x
</button>
) : null}
<button className="add-button" onClick={meta?.addRow}>
Add New +
</button>
</div>
)
}
Additionally, we added a condition to show the Remove Selected
button only if there are selected rows.
Below is the final demo, along with the rest of the styling for the Remove Selected
button and check box input:
table .edit-cell-container {
display: flex;
justify-content: end;
align-items: center;
gap: 4px;
}
table input[type="checkbox"] {
width: 16px;
height: 16px;
}
table .remove-button {
color: #e44747;
}
Complete code and live demo
The complete code is available in this repository. If you liked the tutorial, please star the repository, and feel free to request new features!
Alternatively, you can try the code in the live demo below:
Summary
In this tutorial, we learned how to add and remove rows from a React Table. We also learned how to add a checkbox input to select and remove multiple rows with a single click.
Bye for now 👋