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.
I hope you enjoyed learning from this tutorial, and if you have any questions, please leave a comment below.
Bye for now!