React Table 7 - hooks based library to create beautiful, custom tables in React. Build a fully-fledged table step by step with us, implement the most common features like sorting, filtering, pagination and more. Learn how v7 is different from v6.
React Table 7 - Hooks Approach to Creating Tables in React
React table v7 is a lightweight (5-14kb), headless (100% customizable), and fully controllable tool for building fast and extendable data grids for React. The Library was created by Tanner Linsley, and it is clearly documented on Github.
Differences and Migration from v6 to v7
It’s true that there is a clear and significant difference between the two versions. Version 6 of React Table requires importing the main component and ready-made CSS styles. Both the functionality and the appearance of the UI are controlled by passing the appropriate props to the main component. The latest version of React Table (v7) is headless, which means that it doesn’t provide or support any UI elements. The responsibility for providing and rendering table markup rests solely with us. This gives us the opportunity to build a unique look and feel for our table. React Table v7 uses React Hooks both internally and externally for almost everything. It’s up to us to decide how to build and what functionalities to use. The provided collection of custom React Hooks brings us one step closer to achieving our goal.
You may have already used React Table v6 in the past. This version enjoys great popularity; that said, as the creator himself has pointed out, it could no longer be maintained. Perhaps this very fact or the architecture based on hooks (and thus its performance), will prompt you to switch to a newer version.
Starting with React Table 7
The best approach for starting with React Table v7 is learning by building a simple table, and then expanding it with new functionalities.
In this article, we start by building a simple table from scratch. With each subsequent step, we equip our table with new features such as sorting, filtering, sub-components, pagination and we add bootstrap styling as well.
Below is a photo of the final version we will build together, step by step. You can play with the table here: Demo.
At the end of each step, there’s a link to the current code. Let’s begin then!
Project Setup
We start by creating a new React project using create-react-app
$ npx create-react-app table-example
The next step is to install the react-table library
$ npm install react-table
or
$ yarn add react-table
Once we have our data all ready, the next step is to define the column structure (an array of objects that consists of header - column name and accessor - key in data). For optimization purposes, we wrap our columns in the React useMemo hook.
The first and most important hook we’ll use is useTable
useTable requires an object with two properties: data (our contacts) and columns, which we have previously defined. The hook returns properties that we need to destructre, and we need those to build our table. Their purpose and place are explained in the code below.
We start building our Table in a separate component TableContainer.js This is the basic version of the table, which will be the base for implementing additional functionalities. Please note how our destructured properties from the useTable hook are used.
// TableContainer.js
import React from "react"
import { useTable } from "react-table"
const TableContainer = ({ columns, data }) => {
const {
getTableProps,
getTableBodyProps,
headerGroups,
rows,
prepareRow,
} = useTable({
columns,
data,
})
return (
// If you're curious what props we get as a result of calling our getter functions (getTableProps(), getRowProps())
// Feel free to use console.log() This will help you better understand how react table works underhood.
<table {...getTableProps()}>
<thead>
{headerGroups.map(headerGroup => (
<tr {...headerGroup.getHeaderGroupProps()}>
{headerGroup.headers.map(column => (
<th {...column.getHeaderProps()}>{column.render("Header")}</th>
))}
</tr>
))}
</thead>
<tbody {...getTableBodyProps()}>
{rows.map(row => {
prepareRow(row)
return (
<tr {...row.getRowProps()}>
{row.cells.map(cell => {
return <td {...cell.getCellProps()}>{cell.render("Cell")}</td>
})}
</tr>
)
})}
</tbody>
</table>
)
}
export default TableContainer
All of the code we have written so far, can be found here. Don’t forget to star the repo if you find it useful!
Add Bootstrap Table Style
As we mentioned earlier, version 7 of React Table does not support any UI view. The style of the table is up to us. In our example, we utilize the Bootstrap appearance for this purpose. To do this, we need to install two packages.
$ yarn add bootstrap
$ yarn add reactstrap
The first thing to do is to import bootstrap styles. A bootstrap container is also added in order to position the table into the center.
React Table 7 allows you to define a custom look for each cell. We can do this in the definition for a given column. As far as Table Cell goes, we can render any React Component.
{
Header: 'Color',
accessor: 'color'
// Cell has access to row values. If you are curious what is inside cellProps, you can console log it
Cell: (cellProps) => {
return <YourReactComponent {...cellProps}/>
}
}
In our example we are going to create a new column - Hemisphere, which we use to render the hemisphere sign based on user coordinates. In accessor, we will destructure the latitude and longitude values to determine the user’s hemisphere.
{
Header: 'Hemisphere',
accessor: (values) => {
const { latitude, longitude } = values.location.coordinates;
const first = Number(latitude) > 0 ? 'N' : 'S';
const second = Number(longitude) > 0 ? 'E' : 'W';
return first + '/' + second;
},
// we can also write code below as a separate React Component
Cell: ({ cell }) => {
const { value } = cell;
const pickEmoji = (value) => {
let first = value[0]; // N or S
let second = value[2]; // E or W
const options = ['⇖', '⇗', '⇙', '⇘'];
let num = first === 'N' ? 0 : 2;
num = second === 'E' ? num + 1 : num;
return options[num];
};
return (
<div style={{ textAlign: 'center', fontSize: 18 }}>
{pickEmoji(value)}
</div>
);
}
},
Link to the current code. Don’t forget to star the repo if you find it useful!
Sorting - useSortBy Hook
React Table 7 allows us to easily create sorting for our table. To create sorting, we will need another hook from the React Table hooks collection - useSortBy
We pass the useSortBy hook as a parameter to our main useTable hook. React Table automatically handles sorting in ascending/descending order.
All we need to do is to make a minor change in the way we render column headers. In the column header definition, we invoke the function getSortByToggleProps on a column - which returns an onClick event responsible for changing the sorting direction. We have included generateSortingIndicator - a helper function which returns the sort indicator based on sorting state (ascending/descending/no sorting). Code below.
With this implementation, we can do sorting on each column by default. If we want to disable sorting in a particular column, we need to set the disableSortBy property to true in the column definition.
You can find the code for the table with sorting here. Again, if you haven’t done it already - don’t forget to star the repo if you find it useful!
Filtering - useFilters Hook
The next feature that we will add to our table is column filtering. With the useFilters hook, we can do it an easy and accessible way. Like before, we need to pass the useFilters hook as a parameter to our main useTable hook. useFilters must be placed before useSortBy, otherwise React Table 7 will inform us about that fact in the console. (Error: React Table: The useSortBy plugin hook must be placed after the useFilters plugin hook!)
After connecting the useFilters hook, the next step will be to modify the way of rendering for our <th>. We add the component that will display the view for our filters. On top of that, we wrap the rest of the header cell in <div> so that clicking on our filter does not trigger sorting in a column.
Now, we are going to write our two filters, which we want to use in our table. The first filter DefaultColumnFilter - will render Text Input, which filters based on the entered text (the text filtering functionality is provided by default by the React Table). The second of them, SelectColumnFilter, renders Select Input which allows to choose from the available options.
DefaultColumnFilter - is passed as a default filter for columns to the useTable hook, which means that each of our column uses this filter until it is turned off or another filter is attached.
// TableContainer.js
import { Filter, DefaultColumnFilter } from './filters';
useTable(
{
columns,
data
defaultColumn: { Filter: DefaultColumnFilter }
}
);
SelectColumnFilter - adding the “Filter” prop in Column Definition (overrides the default filter).
// App.js
import { SelectColumnFilter } from './filters';
{
Header: 'Title',
accessor: 'name.title',
Filter: SelectColumnFilter,
filter: 'equals' // by default, filter: 'text', but in our case we don't want to filter options like text, we want to find exact match of selected option.
},
If you don’t want to display any filter in a column, simply add this line of code in for that column:
disableFilters: true
The code for the table with added filters is available here.
Sub Components - useExpanded Hook
The first step is to import the useExpanded hook from React Table and join it with our useTable. Next a visibleColumns prop from useTable is destructured, to make sure that our Sub Component will take 100% of the table width.
To display the Sub-Component, we need to modify the rendering for a table body <tr> We use the value row.isExpanded to determine whether we want to display the Sub-Component.
We use the App.js file to write the renderRowSubComponent function as well as the column definition with emoji indicating whether our row is open or not.
// Add at the beginning of column definition
{
Header: () => null,
id: 'expander', // 'id' is required
Cell: ({ row }) => (
<span {...row.getToggleRowExpandedProps()}>
{row.isExpanded ? '👇' : '👉'}
</span>
)
},
The renderRowSubComponent function, renders a simple Bootstrap Card with contact details. We need to pass it as a prop to TableContainer.
// pass the function 'renderRowSubComponent' as a prop to our TableContainer
// <TableContainer columns={columns} data={data} />
<TableContainer
columns={columns}
data={data}
renderRowSubComponent={renderRowSubComponent}
/>
After implementing the above steps, you should have a working sub-components functionality after clicking the right emoji.
The last hook we are going to implement is usePagination. As always, we need to add usePagination to our useTable. This hook requires destructuring of several additional props that we need to build our pagination. In addition, we define initialState in which we specify how many rows we want to display (pageSize) and from which page we start displaying (pageIndex).
If you have implemented the above steps, you should now have a working pagination for your table. usePagination is the last hook we have used to build our table.
Voilà! We have a ready table that is equipped with some interesting features. The table made in this article is merely an introduction to building more advanced, custom tables with the help of React Table 7!
In fact, there is a whole another spectrum of functionalities that we can get with React Table 7. It’s up to us how we will construct our table. For more information and examples please refer to this great documentation.
Share our work
React
Hooks
React table
Bootstrap
Written By
Bartek Bajda
See related posts
Category
January 5, 2022
Build and release your app with Fastlane!
Continuous Deployment
Fastlane
React Native
Mobile development
Ruby
In this article you will learn how to save your time. Try out this Fastlane setup for both Android and iOS!
Leave your contact info and we’ll be in touch with you shortly
Thank you! Your submission has been received!
Oops! Something went wrong while submitting the form.
By clicking “Accept all”, you agree to the storing of cookies on your device to enhance site navigation, analyze site usage, and assist in our marketing efforts. View our Privacy Policy for more information.