I came across with this little table library (little in size but huge in functionality), React table. I was amazed with the way it simplifies the work. In our projects we need to create sorting functionality on columns of tables. Sometimes we have to implement filters also. You know how complex and difficult that is to implement. But React table get us covered.

If you don’t want to learn React-Table and just want the code for your data, then we have created a GUI tool for that. Use it –

In this article, I will cover the best components of react table. We will learn about filter, sort and pagination. This guide is simple and precise. Let’s get started.

Why to use react table?

Well, more than a million people use this library every month. There must be a reason for its popularity, right? Actually it is awesome. These are the reasons –

  1. It is headless which means you create your own UI. It will give you logic only.
  2. Very light weight. Just ~10kb.
  3. Simplify the complex operations like sorting, filtering, pagination etc.
  4. Optimized for performance (Learn more about react performance optimization).
  5. You can create multiple table headers.
  6. Easily handles row spans and column spans.
  7. My personal favorite is row grouping. You can group rows with similar column value which could also expand like tree. Isn’t it neat?

There are lot more benefits but you got the idea.

Installing React Table

yarn add react-table

You can also use cdn to directly include it in your webpages –

https://unpkg.com/browse/[email protected]/dist/react-table.production.min.js

Most useful hooks of library

The table library provides a number of useful hooks. We are going to use few of them in this article. Some of these hooks are –

  • useTable – The most important and basic one. You can’t work with this library without this hook. It creates all the essentials of table.
  • useFilters – When you need to filter rows based on single column.
  • useGlobalFilters – It helps in filtering rows from all the columns of table.
  • useSortBy – Use this to sort rows.
  • useGroupBy – It helps in grouping all those rows which have same value in a column.
  • useExpanded – When you group rows, you can expand those rows in a beautiful nested tree like structure.
  • usePagination – As the name suggests, it is used for pagination.

How to work with the library?

For designing a table, we require 2 mandatory things –

1. Columns

It is an array of columns or headings for table. For example –

const columns = React.useMemo(() => (
[
  {
	Header: 'Heading 1',
	Footer: 'Footer 1',
	columns: [
		{
			Header: 'Sub Heading 1a',
			accessor: 'firstcolumn',
		},
		{
			Header: 'Sub Heading 1b',
			accessor: 'secondcolumn',
		},
	],
  },{
	Header: 'Heading 2',
	accessor: 'thirdcolumn',
	Footer: 'Footer 2',
  }
]
), [])

A table can have headers, data rows and footers. Although only data rows are required things. In our columns array, we have four object properties –

  1. Header – It is the heading of the column. So, if you have 5 columns then there will be 5 objects in array with key Header.
  2. Footer – Use this key to define the footer column. If you don’t have footer, just don’t use it.
  3. columns – There are situations where you might have multiple rows for headings. Like top most heading could be address but we divide the column further into state, city, zip etc. For cases like those, we can have another array of Columns with key columns.
  4. accessor – This is the mapping of columns with the data. So, if accessor is firstcolumn then it means that column will hold the data from data array whose key is firstcolumn. You will understand it better when we discuss about data array.

The rendered output will look like this –

Showing how header and footer looks like with column array in react table.

But you can see that the output looks a bit wrong. Heading 2 should be in the same row as Heading 1 and Footer 2 should be in Footer 1 row. Let’s correct it.

First see how Heading 1 object looks like –


const columns = React.useMemo(() => (
[
  {
    Header: ‘Heading 1’,
    Footer: ‘Footer 1’,
    columns: [
      {
        Header: ‘Sub Heading 1a’,
        accessor: ‘firstcolumn’,
      },
      {
        Header: ‘Sub Heading 1b’,
        accessor: ‘secondcolumn’,
      },
    ],
  },

{
    Header: ‘Heading 2’,
    accessor: ‘thirdcolumn’,
    Footer: ‘Footer 2’,
  }
]), [])


So, the difference between the objects of Heading 1 and Heading 2 is that there is a nested columns array in Heading 1 which is not present in Heading 2. That’s why we were getting the wrong output. Let’s change Heading 2 object a bit –


{
  Header: ‘Heading 2’,
  
columns: [
    {
      accessor: ‘thirdcolumn’,
    },
  ],

  Footer: ‘Footer 2’,
}


Now we have added a nested columns array with only accessor key. Remember, accessor is required for each column. Output will look like this –

corrected the position of heading 2 and footer 2

It’s pretty clear now how to create the column array. Next we will look at data array.

2. Data

Without data, what will table contain? That’s why we need a data array. In our example, we have 3 columns – firstcolumn, secondcolumn, thirdcolumn. Check the example below –

const data = React.useMemo(() => [
  {
     firstcolumn: 'Row 1 Column 1',
     secondcolumn: 'Row 1 Column 2',
     thirdcolumn: 'Row 1 Column 3',
  },
  {
     firstcolumn: 'Row 2 Column 1',
     secondcolumn: 'Row 2 Column 2',
     thirdcolumn: 'Row 2 Column 3',
  },
], [])

The data array contains objects of data for each row. So, for 1st row the object will contain keys – firstcolumn, secondcolumn, thirdcolumn and values for all of them. The rendered output will look like this –

Data, header, footer rendered

Understanding useTable()

useTable() is the main hook. All other hooks and functionality simply uses this hook only. So, if you want a sort functionality, you will need to add sort parameter into this hook.

The most basic format is –

const {
    getTableProps,
    getTableBodyProps,
    headerGroups,
    footerGroups,
    rows,
    prepareRow,
  } = useTable({ columns, data })

Columns and data we have already seen in previous section.

  1. getTableProps – These are some of the essential props for <table> tag of JSX. Don’t need to go deep into it. Use it like this –
    <table {…getTableProps()}>
  2. getTableBodyProps – Similar to table props, there are some essential props for <tbody> tag. Use them like it –
    <tbody {…getTableBodyProps()}>
  3. headerGroups – This property is actually an array of props for <tr> tag on <thead> and header column values. It’s structure is like this –
    [
        {
            getHeaderGroupProps,
            headers : [
                                  getHeaderProps,
                                  render,
                            ]
        }
    ]
    So, we use it in this way –
    <thead>
        {headerGroups.map(group => (
            <tr {…group.getHeaderGroupProps()}>
                {group.headers.map(column => (
                    <th {…column.getHeaderProps()}>
                        {column.render(‘Header’)}
                    </th>
                ))}
            </tr>
        ))}
    </thead>
  4. footerGroups – It’s similar to headerGroups just the difference is that it is used for <tfoot>. You can use it like this –
    <tfoot>
        {footerGroups.map(group => (
            <tr {…group.getFooterGroupProps()}>
                {group.headers.map(column => (
                    <td {…column.getFooterProps()}>
                        {column.render(‘Footer’)}
                    </td>
                ))}
            </tr>
          ))}
    </tfoot>
  5. rows and prepareRow – It is also similar to headerGroups and footerGroups except it is used for <tbody>. Also, for each row, we are calling prepareRow(row) as some computations are done on it. Use it like this –
    <tbody {…getTableBodyProps()}>
        {rows.map((group, i) => {
            prepareRow(group)
            return (
                    <tr {…group.getRowProps()}>
                        {group.cells.map(column => (
                            <td {…column.getCellProps()}>
                                {column.render(‘Cell’)}
                            </td>
                          ))}
                    </tr>
            )
        })}
    </tbody>

The below table will clear the confusion –

headerGroupsfooterGroupsrows
getHeaderGroupProps()getFooterGroupProps()getRowProps()
group.headers.mapgroup.headers.mapgroup.cells.map
column.getHeaderProps()column.getFooterProps()column.getCellProps()
column.render(‘Header’)column.render(‘Footer’)column.render(‘Cell’)

The whole code will look like this –

import React from "react";
import { useTable } from "react-table";

function Table({ columns, data }) {
  const {
    getTableProps,
    getTableBodyProps,
    headerGroups,
    footerGroups,
    rows,
    prepareRow
  } = useTable({
    columns,
    data
  });

  // Render the UI for your table
  return (
    <table
      {...getTableProps()}
      border={1}
      style={{ borderCollapse: "collapse", width: "100%" }}
    >
      <thead>
        {headerGroups.map((group) => (
          <tr {...group.getHeaderGroupProps()}>
            {group.headers.map((column) => (
              <th {...column.getHeaderProps()}>{column.render("Header")}</th>
            ))}
          </tr>
        ))}
      </thead>
      <tbody {...getTableBodyProps()}>
        {rows.map((row, i) => {
          prepareRow(row);
          return (
            <tr {...row.getRowProps()}>
              {row.cells.map((cell) => {
                return <td {...cell.getCellProps()}>{cell.render("Cell")}</td>;
              })}
            </tr>
          );
        })}
      </tbody>
      <tfoot>
        {footerGroups.map((group) => (
          <tr {...group.getFooterGroupProps()}>
            {group.headers.map((column) => (
              <td {...column.getFooterProps()}>{column.render("Footer")}</td>
            ))}
          </tr>
        ))}
      </tfoot>
    </table>
  );
}

function App() {
  const columns = React.useMemo(
    () => [
      {
        Header: "Heading 1",
        Footer: "Footer 1",
        columns: [
          {
            Header: "Sub Heading 1a",
            accessor: "firstcolumn"
          },
          {
            Header: "Sub Heading 1b",
            accessor: "secondcolumn"
          }
        ]
      },
      {
        Header: "Heading 2",
        Footer: "Footer 2",
        columns: [
          {
            accessor: "thirdcolumn"
          }
        ]
      }
    ],
    []
  );

  const data = React.useMemo(
    () => [
      {
        firstcolumn: "Row 1 Column 1",
        secondcolumn: "Row 1 Column 2",
        thirdcolumn: "Row 1 Column 3"
      },
      {
        firstcolumn: "Row 2 Column 1",
        secondcolumn: "Row 2 Column 2",
        thirdcolumn: "Row 2 Column 3"
      }
    ],
    []
  );

  return <Table columns={columns} data={data} />;
}

export default App;

The output will look like this –

rendered table after using useTable hook

Adding sort functionality using useSortBy

Adding sort functionality is very easy in React-Table. There are only 3 things we have to do –

  1. Include useSortBy hook into useTable hook like this –
    const {
        getTableProps,
        getTableBodyProps,
        headerGroups,
        rows,
        prepareRow
    } = useTable({ columns, data}, useSortBy)
  2. Pass column.getSortByToggleProps() as parameter to getHeaderProps() function in <th> like this –
    <th {…column.getHeaderProps(column.getSortByToggleProps())}>
  3. Add sorting icons on <th> like this –
    <th {…column.getHeaderProps(column.getSortByToggleProps())}>
            {column.render(‘Header’)}
            <span>{
                column.isSorted
                    ? column.isSortedDesc
                          ? ‘ ?’
                          : ‘ ?’
                    : ”
            }</span>

    </th>

Let’s see the code –

import React from "react";
import { useTable, useSortBy } from "react-table";

function Table({ columns, data }) {
  const {
    getTableProps,
    getTableBodyProps,
    headerGroups,
    footerGroups,
    rows,
    prepareRow
  } = useTable({
    columns,
    data
  }, useSortBy);

  // Render the UI for your table
  return (
    <table
      {...getTableProps()}
      border={1}
      style={{ borderCollapse: "collapse", width: "100%" }}
    >
      <thead>
        {headerGroups.map((group) => (
          <tr {...group.getHeaderGroupProps()}>
            {group.headers.map((column) => (
              <th {...column.getHeaderProps(column.getSortByToggleProps())}>{column.render("Header")}<span>{
                column.isSorted
                    ? column.isSortedDesc
                          ? ' ?'
                          : ' ?'
                    : ''
            }</span></th>
            ))}
          </tr>
        ))}
      </thead>
      <tbody {...getTableBodyProps()}>
        {rows.map((row, i) => {
          prepareRow(row);
          return (
            <tr {...row.getRowProps()}>
              {row.cells.map((cell) => {
                return <td {...cell.getCellProps()}>{cell.render("Cell")}</td>;
              })}
            </tr>
          );
        })}
      </tbody>
      <tfoot>
        {footerGroups.map((group) => (
          <tr {...group.getFooterGroupProps()}>
            {group.headers.map((column) => (
              <td {...column.getFooterProps()}>{column.render("Footer")}</td>
            ))}
          </tr>
        ))}
      </tfoot>
    </table>
  );
}

function App() {
  const columns = React.useMemo(
    () => [
      {
        Header: "Heading 1",
        Footer: "Footer 1",
        columns: [
          {
            Header: "Sub Heading 1a",
            accessor: "firstcolumn"
          },
          {
            Header: "Sub Heading 1b",
            accessor: "secondcolumn"
          }
        ]
      },
      {
        Header: "Heading 2",
        Footer: "Footer 2",
        columns: [
          {
            accessor: "thirdcolumn"
          }
        ]
      }
    ],
    []
  );

  const data = React.useMemo(
    () => [
      {
        firstcolumn: "Row 1 Column 1",
        secondcolumn: "Row 1 Column 2",
        thirdcolumn: "Row 1 Column 3"
      },
      {
        firstcolumn: "Row 2 Column 1",
        secondcolumn: "Row 2 Column 2",
        thirdcolumn: "Row 2 Column 3"
      }
    ],
    []
  );

  return <Table columns={columns} data={data} />;
}

export default App;

The rendered output will look like the below image. Check out that the position of rows have changed. Also there is a blue arrow in first column which says that the column is sorted in decreasing order.

rendered table are using useSortBy hook

Adding Pagination using usePagination

React-Table provides a number of handy parameters for pagination. Some of them are –

  • canPreviousPageTrue / False – If any previous page exists.
  • canNextPageTrue / False – If any next page exists.
  • pageCount – Total number of pages. So, if total records are 1000 and page size is 10, then pageCount will be 100.
  • gotoPageFunction to go to a particular page.
  • nextPageFunction to go to next page.
  • previousPageFunction to go to previous page.
  • setPageSizeFunction to set the page size. Value of pageCount and pageSize changes with it.
  • pageSize – Total number of records on single page.
  • pageIndex – Current page number.
  • page – It is the replacement of rows. While the rows contains all the rows of the table, page contains rows available on current page only.

Using all these functions and properties, you can easily create the UI for table pagination. All these are available through useTable hooks when usePagination is passed into it.

const {
    getTableProps,
    getTableBodyProps,
    headerGroups,
    prepareRow,
    page,
    canPreviousPage,
    canNextPage,
    pageCount,
    gotoPage,
    nextPage,
    previousPage,
    setPageSize,
    
state: { pageIndex, pageSize }

} = useTable({columns,data}, usePagination)

See how we can use this in our code example –

import React from "react";
import { useTable, usePagination } from "react-table";

function Table({ columns, data }) {
  const {
    getTableProps,
    getTableBodyProps,
    headerGroups,
    footerGroups,
    prepareRow,
    page,
    canPreviousPage,
    canNextPage,
    pageCount,
    gotoPage,
    nextPage,
    previousPage,
    setPageSize,
    state: { pageIndex, pageSize }
  } = useTable(
    {
      columns,
      data,
      initialState: { pageSize: 2 }
    },
    usePagination
  );

  // Render the UI for your table
  return (
    <>
      <table
        {...getTableProps()}
        border={1}
        style={{ borderCollapse: "collapse", width: "100%" }}
      >
        <thead>
          {headerGroups.map((group) => (
            <tr {...group.getHeaderGroupProps()}>
              {group.headers.map((column) => (
                <th {...column.getHeaderProps()}>{column.render("Header")}</th>
              ))}
            </tr>
          ))}
        </thead>
        <tbody {...getTableBodyProps()}>
          {page.map((row, i) => {
            prepareRow(row);
            return (
              <tr {...row.getRowProps()}>
                {row.cells.map((cell) => {
                  return (
                    <td {...cell.getCellProps()}>{cell.render("Cell")}</td>
                  );
                })}
              </tr>
            );
          })}
        </tbody>
        <tfoot>
          {footerGroups.map((group) => (
            <tr {...group.getFooterGroupProps()}>
              {group.headers.map((column) => (
                <td {...column.getFooterProps()}>{column.render("Footer")}</td>
              ))}
            </tr>
          ))}
        </tfoot>
      </table>
      <div className="pagination">
        <button onClick={() => gotoPage(0)} disabled={!canPreviousPage}>
          {"<<"}
        </button>{" "}
        <button onClick={() => previousPage()} disabled={!canPreviousPage}>
          {"<"}
        </button>{" "}
        <button onClick={() => nextPage()} disabled={!canNextPage}>
          {">"}
        </button>{" "}
        <button onClick={() => gotoPage(pageCount - 1)} disabled={!canNextPage}>
          {">>"}
        </button>{" "}
        <span>
          Page{" "}
          <strong>
            {pageIndex + 1} of {pageCount}
          </strong>{" "}
        </span>
        <span>
          | Go to page:{" "}
          <input
            type="number"
            defaultValue={pageIndex + 1}
            onChange={(e) => {
              const page = e.target.value ? Number(e.target.value) - 1 : 0;
              gotoPage(page);
            }}
            style={{ width: "100px" }}
          />
        </span>{" "}
        <select
          value={pageSize}
          onChange={(e) => {
            setPageSize(Number(e.target.value));
          }}
        >
          {[2, 10, 20, 30, 40, 50].map((pageSize) => (
            <option key={pageSize} value={pageSize}>
              Show {pageSize}
            </option>
          ))}
        </select>
      </div>
    </>
  );
}

function App() {
  const columns = React.useMemo(
    () => [
      {
        Header: "Heading 1",
        Footer: "Footer 1",
        columns: [
          {
            Header: "Sub Heading 1a",
            accessor: "firstcolumn"
          },
          {
            Header: "Sub Heading 1b",
            accessor: "secondcolumn"
          }
        ]
      },
      {
        Header: "Heading 2",
        Footer: "Footer 2",
        columns: [
          {
            accessor: "thirdcolumn"
          }
        ]
      }
    ],
    []
  );

  const data = React.useMemo(
    () => [
      {
        firstcolumn: "Row 1 Column 1",
        secondcolumn: "Row 1 Column 2",
        thirdcolumn: "Row 1 Column 3"
      },
      {
        firstcolumn: "Row 2 Column 1",
        secondcolumn: "Row 2 Column 2",
        thirdcolumn: "Row 2 Column 3"
      },
      {
        firstcolumn: "Row 3 Column 1",
        secondcolumn: "Row 3 Column 2",
        thirdcolumn: "Row 3 Column 3"
      },
      {
        firstcolumn: "Row 4 Column 1",
        secondcolumn: "Row 4 Column 2",
        thirdcolumn: "Row 4 Column 3"
      },
      {
        firstcolumn: "Row 5 Column 1",
        secondcolumn: "Row 5 Column 2",
        thirdcolumn: "Row 5 Column 3"
      },
      {
        firstcolumn: "Row 6 Column 1",
        secondcolumn: "Row 6 Column 2",
        thirdcolumn: "Row 6 Column 3"
      },
      {
        firstcolumn: "Row 7 Column 1",
        secondcolumn: "Row 7 Column 2",
        thirdcolumn: "Row 7 Column 3"
      },
      {
        firstcolumn: "Row 8 Column 1",
        secondcolumn: "Row 8 Column 2",
        thirdcolumn: "Row 8 Column 3"
      },
      {
        firstcolumn: "Row 9 Column 1",
        secondcolumn: "Row 9 Column 2",
        thirdcolumn: "Row 9 Column 3"
      },
      {
        firstcolumn: "Row 10 Column 1",
        secondcolumn: "Row 10 Column 2",
        thirdcolumn: "Row 10 Column 3"
      },
      {
        firstcolumn: "Row 11 Column 1",
        secondcolumn: "Row 11 Column 2",
        thirdcolumn: "Row 11 Column 3"
      },
      {
        firstcolumn: "Row 12 Column 1",
        secondcolumn: "Row 12 Column 2",
        thirdcolumn: "Row 12 Column 3"
      }
    ],
    []
  );

  return <Table columns={columns} data={data} />;
}

export default App;

Check out the demo of pagination –

Adding Filters using useFilters in React Table

There are two hooks which library provides for filters – useFilters and useGlobalFilter. Before moving forward, we need to understand the basic requirements for filters in react table.

  1. A UI to let users filter on columns. For example –

    filter input fields ui

  2. A criteria for matching data. For example – ‘equals‘, ‘between‘, ‘includes‘, or some custom function for your own criteria.

We set both of these in our columns array where we are setting accessor.

{
    Header: ‘Sub Heading 1a’,
    accessor: ‘firstcolumn’,
    Filter: UIComponentForFilter,
    filter: ‘filter criteria’,
}

There is also an option to create default filter UI as well as criteria. It is used when you don’t provide Filter or filter keys to columns object.

const defaultColumn = React.useMemo(
  () => ({
      Filter: DefaultColumnFilter,
  }),
  []
)

function DefaultColumnFilter({
  column: { filterValue, preFilteredRows, setFilter },
}) {
  const count = preFilteredRows.length

  return (
    <input
      value={filterValue || ''}
      onChange={e => {
        setFilter(e.target.value || undefined) // Set undefined to remove the filter entirely
      }}
      placeholder={`Search ${count} records...`}
    />
  )
}

This will render the default UI which is an input field. The library passes a number of params but we only require 3 here –

  1. filterValue – The current value of the input field which is empty when you are not typing anything to filter.
  2. setFilter – It is a function which sets the filterValue. Whatever you type in input field, that will become the filterValue.
  3. preFilteredRows – It’s a collection of rows which have passed already (if any) used filter criteria. Like, you have 100 rows and after assigning a filter for age, you now left with 30 rows, then preFilteredRows will contain those 30 rows. So, another column will filter over these 30 rows and not 100.

Now let’s create a default filter criteria –

const filterTypes = React.useMemo(
    () => ({
      text: (rows, id, filterValue) => {
        return rows.filter(row => {
          const rowValue = row.values[id]
          return rowValue !== undefined
            ? String(rowValue)
                .toLowerCase()
                .startsWith(String(filterValue).toLowerCase())
            : true
        })
      },
    }),
    []
  )

This criteria will filter rows based on startsWith characters. So, all the values in the column which starts with our filterValue will be shown. This is the most common use case in search.

Also, you need to render the UI below the heading in the table. Like this –

<th {…column.getHeaderProps()}>
    {column.render(“Header”)}
    <div>{column.canFilter ? column.render(“Filter”) : null}</div>
</th>

useTable structure with useFilters

const {
    getTableProps,
    getTableBodyProps,
    headerGroups,
    rows,
    prepareRow,
    state,
    preGlobalFilteredRows,
    setGlobalFilter

} = useTable(
    {
        columns,
        data,
        defaultColumn,
        filterTypes

    },
    useFilters,
    useGlobalFilter

)

Here you can see that we have provided our default filter UI (defaultColumn) and filter criteria (filterTypes) in useTable hook.

Also we have included useGlobalFilter. This is useful when you want a search functionality in your table. preGlobalFilteredRows, setGlobalFilter are the properties of useGlobalFilter only.

Basic example of filter

import React from "react";
import { useTable, useFilters } from "react-table";

function Table({ columns, data }) {
  const defaultColumn = React.useMemo(
    () => ({
      Filter: DefaultColumnFilter
    }),
    []
  );

  function DefaultColumnFilter({
    column: { filterValue, preFilteredRows, setFilter }
  }) {
    const count = preFilteredRows.length;

    return (
      <input
        value={filterValue || ""}
        onChange={(e) => {
          setFilter(e.target.value || undefined); // Set undefined to remove the filter entirely
        }}
        placeholder={`Search ${count} records...`}
      />
    );
  }

  const filterTypes = React.useMemo(
    () => ({
      text: (rows, id, filterValue) => {
        return rows.filter((row) => {
          const rowValue = row.values[id];
          return rowValue !== undefined
            ? String(rowValue)
                .toLowerCase()
                .startsWith(String(filterValue).toLowerCase())
            : true;
        });
      }
    }),
    []
  );

  const {
    getTableProps,
    getTableBodyProps,
    headerGroups,
    footerGroups,
    rows,
    prepareRow
  } = useTable(
    {
      columns,
      data,
      defaultColumn,
      filterTypes
    },
    useFilters
  );

  // Render the UI for your table
  return (
    <table
      {...getTableProps()}
      border={1}
      style={{ borderCollapse: "collapse", width: "100%" }}
    >
      <thead>
        {headerGroups.map((group) => (
          <tr {...group.getHeaderGroupProps()}>
            {group.headers.map((column) => (
              <th {...column.getHeaderProps()}>
                {column.render("Header")}
                <div>{column.canFilter ? column.render("Filter") : null}</div>
              </th>
            ))}
          </tr>
        ))}
      </thead>
      <tbody {...getTableBodyProps()}>
        {rows.map((row, i) => {
          prepareRow(row);
          return (
            <tr {...row.getRowProps()}>
              {row.cells.map((cell) => {
                return <td {...cell.getCellProps()}>{cell.render("Cell")}</td>;
              })}
            </tr>
          );
        })}
      </tbody>
      <tfoot>
        {footerGroups.map((group) => (
          <tr {...group.getFooterGroupProps()}>
            {group.headers.map((column) => (
              <td {...column.getFooterProps()}>{column.render("Footer")}</td>
            ))}
          </tr>
        ))}
      </tfoot>
    </table>
  );
}

function App() {
  const columns = React.useMemo(
    () => [
      {
        Header: "Heading 1",
        Footer: "Footer 1",
        columns: [
          {
            Header: "Sub Heading 1a",
            accessor: "firstcolumn"
          },
          {
            Header: "Sub Heading 1b",
            accessor: "secondcolumn"
          }
        ]
      },
      {
        Header: "Heading 2",
        Footer: "Footer 2",
        columns: [
          {
            accessor: "thirdcolumn"
          }
        ]
      }
    ],
    []
  );

  const data = React.useMemo(
    () => [
      {
        firstcolumn: "Row 1 Column 1",
        secondcolumn: "Row 1 Column 2",
        thirdcolumn: "Row 1 Column 3"
      },
      {
        firstcolumn: "Row 2 Column 1",
        secondcolumn: "Row 2 Column 2",
        thirdcolumn: "Row 2 Column 3"
      },
      {
        firstcolumn: "Row 3 Column 1",
        secondcolumn: "Row 3 Column 2",
        thirdcolumn: "Row 3 Column 3"
      },
      {
        firstcolumn: "Row 4 Column 1",
        secondcolumn: "Row 4 Column 2",
        thirdcolumn: "Row 4 Column 3"
      },
      {
        firstcolumn: "Row 5 Column 1",
        secondcolumn: "Row 5 Column 2",
        thirdcolumn: "Row 5 Column 3"
      },
      {
        firstcolumn: "Row 6 Column 1",
        secondcolumn: "Row 6 Column 2",
        thirdcolumn: "Row 6 Column 3"
      },
      {
        firstcolumn: "Row 7 Column 1",
        secondcolumn: "Row 7 Column 2",
        thirdcolumn: "Row 7 Column 3"
      },
      {
        firstcolumn: "Row 8 Column 1",
        secondcolumn: "Row 8 Column 2",
        thirdcolumn: "Row 8 Column 3"
      },
      {
        firstcolumn: "Row 9 Column 1",
        secondcolumn: "Row 9 Column 2",
        thirdcolumn: "Row 9 Column 3"
      },
      {
        firstcolumn: "Row 10 Column 1",
        secondcolumn: "Row 10 Column 2",
        thirdcolumn: "Row 10 Column 3"
      },
      {
        firstcolumn: "Row 11 Column 1",
        secondcolumn: "Row 11 Column 2",
        thirdcolumn: "Row 11 Column 3"
      },
      {
        firstcolumn: "Row 12 Column 1",
        secondcolumn: "Row 12 Column 2",
        thirdcolumn: "Row 12 Column 3"
      }
    ],
    []
  );

  return <Table columns={columns} data={data} />;
}

export default App;

Code Demo

Using useGlobalFilter – Search functionality

To use global filter we can use this code –

import React from "react";
import { useTable, useGlobalFilter, useAsyncDebounce } from "react-table";

function GlobalFilter({
  preGlobalFilteredRows,
  globalFilter,
  setGlobalFilter,
}) {
  const count = preGlobalFilteredRows.length
  const [value, setValue] = React.useState(globalFilter)
  const onChange = useAsyncDebounce(value => {
    setGlobalFilter(value || undefined)
  }, 200)

  return (
    <span>
      Search:{' '}
      <input
        value={value || ""}
        onChange={e => {
          setValue(e.target.value);
          onChange(e.target.value);
        }}
        placeholder={`${count} records...`}
        style={{
          fontSize: '1.1rem',
          border: '0',
        }}
      />
    </span>
  )
}

function Table({ columns, data }) {
  
const {
    getTableProps,
    getTableBodyProps,
    headerGroups,
    footerGroups,
    rows,
    prepareRow,
    state,
    preGlobalFilteredRows,
    setGlobalFilter,
  } = useTable(
    {
      columns,
      data,
    },
    useGlobalFilter
  );

  // Render the UI for your table
  return (
    <table
      {...getTableProps()}
      border={1}
      style={{ borderCollapse: "collapse", width: "100%" }}
    >
      <thead>
        {headerGroups.map((group) => (
          <tr {...group.getHeaderGroupProps()}>
            {group.headers.map((column) => (
              <th {...column.getHeaderProps()}>
                {column.render("Header")}
              </th>
            ))}
          </tr>
        ))}
        <tr>
            <th
              colSpan={100}
              style={{
                textAlign: 'left',
                padding: 10,
                background: 'yellow'
              }}
            >
              <GlobalFilter
                preGlobalFilteredRows={preGlobalFilteredRows}
                globalFilter={state.globalFilter}
                setGlobalFilter={setGlobalFilter}
              />
            </th>
          </tr>
      </thead>
      <tbody {...getTableBodyProps()}>
        {rows.map((row, i) => {
          prepareRow(row);
          return (
            <tr {...row.getRowProps()}>
              {row.cells.map((cell) => {
                return <td {...cell.getCellProps()}>{cell.render("Cell")}</td>;
              })}
            </tr>
          );
        })}
      </tbody>
      <tfoot>
        {footerGroups.map((group) => (
          <tr {...group.getFooterGroupProps()}>
            {group.headers.map((column) => (
              <td {...column.getFooterProps()}>{column.render("Footer")}</td>
            ))}
          </tr>
        ))}
      </tfoot>
    </table>
  );
}

function App() {
  const columns = React.useMemo(
    () => [
      {
        Header: "Heading 1",
        Footer: "Footer 1",
        columns: [
          {
            Header: "Sub Heading 1a",
            accessor: "firstcolumn"
          },
          {
            Header: "Sub Heading 1b",
            accessor: "secondcolumn"
          }
        ]
      },
      {
        Header: "Heading 2",
        Footer: "Footer 2",
        columns: [
          {
            accessor: "thirdcolumn"
          }
        ]
      }
    ],
    []
  );

  const data = React.useMemo(
    () => [
      {
        firstcolumn: "Row 1 Column 1",
        secondcolumn: "Row 1 Column 2",
        thirdcolumn: "Row 1 Column 3"
      },
      {
        firstcolumn: "Row 2 Column 1",
        secondcolumn: "Row 2 Column 2",
        thirdcolumn: "Row 2 Column 3"
      },
      {
        firstcolumn: "Row 3 Column 1",
        secondcolumn: "Row 3 Column 2",
        thirdcolumn: "Row 3 Column 3"
      },
      {
        firstcolumn: "Row 4 Column 1",
        secondcolumn: "Row 4 Column 2",
        thirdcolumn: "Row 4 Column 3"
      },
      {
        firstcolumn: "Row 5 Column 1",
        secondcolumn: "Row 5 Column 2",
        thirdcolumn: "Row 5 Column 3"
      },
      {
        firstcolumn: "Row 6 Column 1",
        secondcolumn: "Row 6 Column 2",
        thirdcolumn: "Row 6 Column 3"
      },
      {
        firstcolumn: "Row 7 Column 1",
        secondcolumn: "Row 7 Column 2",
        thirdcolumn: "Row 7 Column 3"
      },
      {
        firstcolumn: "Row 8 Column 1",
        secondcolumn: "Row 8 Column 2",
        thirdcolumn: "Row 8 Column 3"
      },
      {
        firstcolumn: "Row 9 Column 1",
        secondcolumn: "Row 9 Column 2",
        thirdcolumn: "Row 9 Column 3"
      },
      {
        firstcolumn: "Row 10 Column 1",
        secondcolumn: "Row 10 Column 2",
        thirdcolumn: "Row 10 Column 3"
      },
      {
        firstcolumn: "Row 11 Column 1",
        secondcolumn: "Row 11 Column 2",
        thirdcolumn: "Row 11 Column 3"
      },
      {
        firstcolumn: "Row 12 Column 1",
        secondcolumn: "Row 12 Column 2",
        thirdcolumn: "Row 12 Column 3"
      }
    ],
    []
  );

  return <Table columns={columns} data={data} />;
}

export default App;

Demo –

You may use your own custom UI components as well as filter criteria function. The below demo will help you in understanding this –

    Tweet this to help others

Comments

About the Author

akamit

I am Akash Mittal, an overall computer scientist. If you want to guest post, need help in your projects, want to advertise, Feel free to contact me at [email protected]

View All Articles