Building and styling tables with react-table v7

bnevilleoneill

Brian Neville-O'Neill

Posted on April 29, 2020

Building and styling tables with react-table v7

Written by John Au-Yeung✏️

It’s widely acknowledged that creating a table with React is a pain. No surprise, then, that there are many libraries to make creating tables easier for React apps.

One of these packages that aims to make our lives easier is react-table. It provides a modern, Hooks-based API to let us create tables with React with little hassle.

In this article, we’ll look at how to use react-table to add tables to our React app.

Creating a basic table

Creating a basic table in a React app is easy with react-table. Run the following to install it:

npm i react-table
Enter fullscreen mode Exit fullscreen mode

Then we can use it as follows:

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

const data = [
  { firstName: "jane", lastName: "doe", age: 20 },
  { firstName: "john", lastName: "smith", age: 21 }
];

const columns = [
  {
    Header: "Name",
    columns: [
      {
        Header: "First Name",
        accessor: "firstName"
      },
      {
        Header: "Last Name",
        accessor: "lastName"
      }
    ]
  },
  {
    Header: "Other Info",
    columns: [
      {
        Header: "Age",
        accessor: "age"
      }
    ]
  }
];

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

  return (
    <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, i) => {
          prepareRow(row);
          return (
            <tr {...row.getRowProps()}>
              {row.cells.map(cell => {
                return <td {...cell.getCellProps()}>{cell.render("Cell")}</td>;
              })}
            </tr>
          );
        })}
      </tbody>
    </table>
  );
};

export default function App() {
  return (
    <div className="App">
      <Table columns={columns} data={data} />
    </div>
  );
}
Enter fullscreen mode Exit fullscreen mode

In the code above, we imported the useTable Hook from the react-table package. Then we created the data to populate the table with data:

const data = [
  { firstName: "jane", lastName: "doe", age: 20 },
  { firstName: "john", lastName: "smith", age: 21 }
];
Enter fullscreen mode Exit fullscreen mode

We just put properties in objects to add additional data for a table row.

LogRocket Free Trial Banner

We can create columns in a list with the following code:

const columns = [
  {
    Header: "Name",
    columns: [
      {
        Header: "First Name",
        accessor: "firstName"
      },
      {
        Header: "Last Name",
        accessor: "lastName"
      }
    ]
  },
  {
    Header: "Other Info",
    columns: [
      {
        Header: "Age",
        accessor: "age"
      }
    ]
  }
];
Enter fullscreen mode Exit fullscreen mode

The Header property has the string for the names that’ll be displayed, and the accessor property is the property name that’s in the array entry objects.

In the Table component code, we have:

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

  return (
    <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, i) => {
          prepareRow(row);
          return (
            <tr {...row.getRowProps()}>
              {row.cells.map(cell => {
                return <td {...cell.getCellProps()}>{cell.render("Cell")}</td>;
              })}
            </tr>
          );
        })}
      </tbody>
    </table>
  );
};
Enter fullscreen mode Exit fullscreen mode

The useTable Hook takes the column and data from the props, which originate from those objects and arrays that we defined earlier. We get the functions from the getTableProps and getTableBodyProps from the object returned from the useTable Hook.

The getHeaderProps() function is called inside the th tags and spread to populate the headers. With this, we pass the props returned by the getTableBodyProps() function to tbody to spread the props to properly style and align the columns.

The prepareRow(row); returned from the useTable Hook creates the row entries, which can be automatically populated after the call to the function changes the row object in place.

Then we have:

{rows.map((row, i) => {
  prepareRow(row);
  return (
   <tr {...row.getRowProps()}>
     {row.cells.map(cell => {
       return <td {...cell.getCellProps()}>{cell.render("Cell")}</td>;
     })}
   </tr>
  );
})}
Enter fullscreen mode Exit fullscreen mode

This automatically populates the cells by getting the items from the getCellProps() method and then populating the values from the returned object. We called cell.render("Cell") to render each td as a cell.

Finally, in App, we used the Table component, which takes the column and data props. The values are the columns and data objects that we created earlier.

The items displayed in the table in two panes. The left pane has the Name header with two columns: First Name and Last Name. Then, the right pane has the Other Info heading with the Age column.

Adding a footer

We can add a footer by adding a Footer property to out column objects. We can write the following code to do that:

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

const data = [
  { firstName: "jane", lastName: "doe", age: 20 },
  { firstName: "john", lastName: "smith", age: 21 }
];

const columns = [
  {
    Header: "Name",
    Footer: "Name",
    columns: [
      {
        Header: "First Name",
        accessor: "firstName"
      },
      {
        Header: "Last Name",
        accessor: "lastName"
      }
    ]
  },
  {
    Header: "Other Info",
    Footer: "Other Info",
    columns: [
      {
        Header: "Age",
        accessor: "age",
        Footer: info => {
          const total = React.useMemo(
            () => info.rows.reduce((sum, row) => row.values.age + sum, 0),
            [info.rows]
          );

          return <>Average Age: {total / info.rows.length}</>;
        }
      }
    ]
  }
];

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

  return (
    <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, 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>
  );
};

export default function App() {
  return (
    <div className="App">
      <Table columns={columns} data={data} />
    </div>
  );
}
Enter fullscreen mode Exit fullscreen mode

In the code above, we added the Footer property to the columns array as follows:

const columns = [
  {
    Header: "Name",
    Footer: "Name",
    columns: [
      {
        Header: "First Name",
        accessor: "firstName"
      },
      {
        Header: "Last Name",
        accessor: "lastName"
      }
    ]
  },
  {
    Header: "Other Info",
    Footer: "Other Info",
    columns: [
      {
        Header: "Age",
        accessor: "age",
        Footer: info => {
          const total = React.useMemo(
            () => info.rows.reduce((sum, row) => row.values.age + sum, 0),
            [info.rows]
          );

          return <>Average Age: {total / info.rows.length}</>;
        }
      }
    ]
  }
];
Enter fullscreen mode Exit fullscreen mode

We added the Footer property to the top-level of each object.

Also, we add a function for the Footer property in the object for the Age column.

The Footer property in the object for the Age column is:

info => {
  const total = React.useMemo(
    () => info.rows.reduce((sum, row) => row.values.age + sum, 0),
    [info.rows]
  );

  return <>Average Age: {total / info.rows.length}</>;
}
Enter fullscreen mode Exit fullscreen mode

It takes the info object, which has all the table data. Then we summed by all the age property values for each entry and divided it by info.row.length to return the average age. This is displayed at the bottom of the table below the Age column.

The average will change as the row changes since we have [info.rows], which watches the rows for changing values and recomputes the value when the rows change.

Sorting

We can add sorting to a table by calling a few functions. We have to pass in the useSortBy Hook as the second argument of the useTable Hook to get sorting capability in our table.

Then, in our JSX code, we have to pass in column.getSortByToggleProps() to column.getHeaderProps to get the sorting order of the columns in the rendered column.

We can check the order in which a column is sorted by using the column.isSorted and column.isSortedDesc to check if a column is sorted by ascending or descending order, respectively.

Also, we can add a sortType property to the column array entries so we can specify the sort type. For instance, we can write the following code to add basic sorting to our table:

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

const data = [
  { firstName: "jane", lastName: "doe" },
  { firstName: "john", lastName: "smith" }
];

const columns = [
  {
    Header: "Name",
    columns: [
      {
        Header: "First Name",
        accessor: "firstName",
        sortType: "basic"
      },
      {
        Header: "Last Name",
        accessor: "lastName",
        sortType: "basic"
      }
    ]
  }
];

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

  return (
    <table {...getTableProps()}>
      <thead>
        {headerGroups.map(headerGroup => (
          <tr {...headerGroup.getHeaderGroupProps()}>
            {headerGroup.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>
    </table>
  );
};

export default function App() {
  return (
    <div className="App">
      <Table columns={columns} data={data} />
    </div>
  );
}
Enter fullscreen mode Exit fullscreen mode

In the code above, we specified that sortType is 'basic' so that words are sorted alphabetically and numbers are sorted numerically.

Then we rendered the thead by writing:

<thead>
  {headerGroups.map(headerGroup => (
    <tr {...headerGroup.getHeaderGroupProps()}>
      {headerGroup.headers.map(column => (
        <th {...column.getHeaderProps(column.getSortByToggleProps())}>
          {column.render("Header")}
          <span>
            {column.isSorted ? (column.isSortedDesc ? " 🔽" : " 🔼") : ""}
          </span>
        </th>
      ))}
    </tr>
  ))}
</thead>
Enter fullscreen mode Exit fullscreen mode

This adds icons for indicating the sort order of each column and getting the order by which the column is sorted.

After writing that code, we’ll see a sort button to the right of our column headings that we can click on to sort the columns.

Filtering

Filtering is more complex than creating a simple table or sorting. We have to create a component with an input control we can use to filter our items. The input component will take the functions that are returned from the useTable as props and call them in the inputs.

For instance, we can write the following code to do that:

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

const data = [
  { firstName: "jane", lastName: "doe", age: 20 },
  { firstName: "john", lastName: "smith", age: 21 }
];

const columns = [
  {
    Header: "Name",
    columns: [
      {
        Header: "First Name",
        accessor: "firstName",
        filter: "text"
      },
      {
        Header: "Last Name",
        accessor: "lastName",
        filter: "text"
      }
    ]
  },
  {
    Header: "Other Info",
    columns: [
      {
        Header: "Age",
        accessor: "age",
        filter: "text"
      }
    ]
  }
];

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

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

const GlobalFilter = ({
  preGlobalFilteredRows,
  globalFilter,
  setGlobalFilter
}) => {
  const count = preGlobalFilteredRows && preGlobalFilteredRows.length;

  return (
    <span>
      Search:{" "}
      <input
        value={globalFilter || ""}
        onChange={e => {
          setGlobalFilter(e.target.value || undefined); // Set undefined to remove the filter entirely
        }}
        placeholder={`${count} records...`}
        style={{
          border: "0"
        }}
      />
    </span>
  );
};

const Table = ({ columns, data }) => {
  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 defaultColumn = React.useMemo(
    () => ({
      Filter: DefaultColumnFilter
    }),
    []
  );

  const {
    getTableProps,
    getTableBodyProps,
    headerGroups,
    rows,
    prepareRow,
    state,
    visibleColumns,
    preGlobalFilteredRows,
    setGlobalFilter
  } = useTable(
    {
      columns,
      data,
      defaultColumn,
      filterTypes
    },
    useFilters,
    useGlobalFilter
  );

  return (
    <table {...getTableProps()}>
      <thead>
        {headerGroups.map(headerGroup => (
          <tr {...headerGroup.getHeaderGroupProps()}>
            {headerGroup.headers.map(column => (
              <th {...column.getHeaderProps()}>
                {column.render("Header")}
                <div>{column.canFilter ? column.render("Filter") : null}</div>
              </th>
            ))}
          </tr>
        ))}
        <tr>
          <th
            colSpan={visibleColumns.length}
            style={{
              textAlign: "left"
            }}
          >
            <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>
    </table>
  );
};

export default function App() {
  return (
    <div className="App">
      <Table columns={columns} data={data} />
    </div>
  );
}
Enter fullscreen mode Exit fullscreen mode

In the code above, we added the GlobalFilter component as follows:

const GlobalFilter = ({
  preGlobalFilteredRows,
  globalFilter,
  setGlobalFilter
}) => {
  const count = preGlobalFilteredRows && preGlobalFilteredRows.length;

  return (
    <span>
      Search:{" "}
      <input
        value={globalFilter || ""}
        onChange={e => {
          setGlobalFilter(e.target.value || undefined);
        }}
        placeholder={`${count} records...`}
        style={{
          border: "0"
        }}
      />
    </span>
  );
};
Enter fullscreen mode Exit fullscreen mode

This is used to search all the columns present in the data by calling the setGlobalFilter function that’s passed in as props. The preGlobalFilteredRows is an array in which we can count the number of rows that we’re searching for.

Then, in the Table component, we added the following code:

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 defaultColumn = React.useMemo(
  () => ({
    Filter: DefaultColumnFilter
  }),
  []
);

const {
  getTableProps,
  getTableBodyProps,
  headerGroups,
  rows,
  prepareRow,
  state,
  visibleColumns,
  preGlobalFilteredRows,
  setGlobalFilter
} = useTable(
  {
    columns,
    data,
    defaultColumn,
    filterTypes
  },
  useFilters,
  useGlobalFilter
);
Enter fullscreen mode Exit fullscreen mode

The defaultColumn has a cached object, which has the DefaultColumnFilter set as follows:

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

  return (
    <input
      value={filterValue || ""}
      onChange={e => {
        setFilter(e.target.value || undefined);
      }}
      placeholder={`Search ${count} records...`}
    />
  );
};
Enter fullscreen mode Exit fullscreen mode

The defaultColumn caches the input component that’s used to search individual columns. We also have the filterTypes constant, which has the cached value of the filter we used to search our table.

We have an object with the text method, which is used to search the entries that we’re looking for as we type. In the method, we called filter on rows to return the items that start with the given search string, which is stored in filterValue.

We also used more of the returned properties from the useTable Hook and passed in more arguments to the Hook, including the useFilters and useGlobalFilter Hooks to let us filter by column and globally, respectively.

Also, we added the defaultColumn and filterTypes objects to the object in the first argument to let us set the component we’ll use to do the filtering by default. filterTypes lets us set the value to the name of our function that we created for returning filtered data from our array of data.

In the end, we get two inputs to filter each column individually and one that can filter the items from all columns globally.

Pagination

We can add pagination using the usePagination Hook, which is passed in as the argument for the useTable Hook.

The useTable Hook then returns a bunch of pagination-related variables that we used to track the pagination and navigate to different pages.

To make a simple table with pagination, we can write the following code:

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

const firstNames = ["jane", "john", "alex"];
const lastName = ["smith", "jones"];

const data = Array(100)
  .fill()
  .map(a => ({
    firstName: firstNames[Math.floor(Math.random() * firstNames.length)],
    lastName: lastName[Math.floor(Math.random() * lastName.length)],
    age: Math.ceil(75 * Math.random())
  }));

const columns = [
  {
    Header: "Name",
    columns: [
      {
        Header: "First Name",
        accessor: "firstName"
      },
      {
        Header: "Last Name",
        accessor: "lastName"
      }
    ]
  },
  {
    Header: "Other Info",
    columns: [
      {
        Header: "Age",
        accessor: "age"
      }
    ]
  }
];

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

  return (
    <>
      <table {...getTableProps()}>
        <thead>
          {headerGroups.map(headerGroup => (
            <tr {...headerGroup.getHeaderGroupProps()}>
              {headerGroup.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>
      </table>
      <div>
        <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 {pageOptions.length}
          </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));
          }}
        >
          {[10, 20, 30, 40, 50].map(pageSize => (
            <option key={pageSize} value={pageSize}>
              Show {pageSize}
            </option>
          ))}
        </select>
      </div>
    </>
  );
};

export default function App() {
  return (
    <div className="App">
      <Table columns={columns} data={data} />
    </div>
  );
}
Enter fullscreen mode Exit fullscreen mode

In the code above, we generated 100 array entries with random names and pages of people. The headers are the same as the simple table example above.

In the Table component, we have:

const {
  getTableProps,
  getTableBodyProps,
  headerGroups,
  prepareRow,
  page,
  canPreviousPage,
  canNextPage,
  pageOptions,
  pageCount,
  gotoPage,
  nextPage,
  previousPage,
  setPageSize,
  state: { pageIndex, pageSize }
} = useTable(
  {
    columns,
    data,
    initialState: { pageIndex: 0 }
  },
  usePagination
);
Enter fullscreen mode Exit fullscreen mode

With this, we can get various pieces of data we need for pagination, like pageSize to change the number of items displayed on each page.

canPreviousPage and canNextPage tell us whether we can move to the previous or next page respectively. pageCount has the total page count, and gotoPage is a function that lets us skip to the given page number. previousPage and nextPage are also functions that let us navigate to the given page.

They’re used in the following div to navigate between pages:

<div>
  <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 {pageOptions.length}
    </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));
    }}
  >
    {[10, 20, 30, 40, 50].map(pageSize => (
      <option key={pageSize} value={pageSize}>
        Show {pageSize}
      </option>
    ))}
  </select>
</div>
Enter fullscreen mode Exit fullscreen mode

Then we get a table with the same columns as in the example above, but with pagination buttons added. We can also use the dropdown to change the size of each page.

Integration with Material UI

The react-table package integrates with Material UI to let us create a table that follows the Material Design specification.

To install Material UI, we run:

npm install @material-ui/core
Enter fullscreen mode Exit fullscreen mode

Then we can use Material UI’s table components with react-table to create the table as follows:

import React from "react";
import { useTable } from "react-table";
import MaUTable from "@material-ui/core/Table";
import TableBody from "@material-ui/core/TableBody";
import TableCell from "@material-ui/core/TableCell";
import TableHead from "@material-ui/core/TableHead";
import TableRow from "@material-ui/core/TableRow";

const data = [
  { firstName: "jane", lastName: "doe", age: 20 },
  { firstName: "john", lastName: "smith", age: 21 }
];

const columns = [
  {
    Header: "Name",
    columns: [
      {
        Header: "First Name",
        accessor: "firstName"
      },
      {
        Header: "Last Name",
        accessor: "lastName"
      }
    ]
  },
  {
    Header: "Other Info",
    columns: [
      {
        Header: "Age",
        accessor: "age"
      }
    ]
  }
];

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

  return (
    <MaUTable {...getTableProps()}>
      <TableHead>
        {headerGroups.map(headerGroup => (
          <TableRow {...headerGroup.getHeaderGroupProps()}>
            {headerGroup.headers.map(column => (
              <TableCell {...column.getHeaderProps()}>
                {column.render("Header")}
              </TableCell>
            ))}
          </TableRow>
        ))}
      </TableHead>
      <TableBody>
        {rows.map((row, i) => {
          prepareRow(row);
          return (
            <TableRow {...row.getRowProps()}>
              {row.cells.map(cell => {
                return (
                  <TableCell {...cell.getCellProps()}>
                    {cell.render("Cell")}
                  </TableCell>
                );
              })}
            </TableRow>
          );
        })}
      </TableBody>
    </MaUTable>
  );
};

export default function App() {
  return (
    <div className="App">
      <Table columns={columns} data={data} />
    </div>
  );
}
Enter fullscreen mode Exit fullscreen mode

In the code above, we used the Material UI components to render the table, but the data is populated by react-table. We called the same methods we used in the simple table example to populate the rows and columns with data.

Therefore, we get the same data and columns as the simple table example, but it adheres to Material Design instead of having no styling.

Conclusion

As we can see, react-table is capable of creating tables with lots of capabilities without having to create everything from scratch ourselves.

It provides us with a Hooks-based API to create tables, which is important since some devs would like to switch to using function components with Hooks now.

There are many more examples to showcase what react-table can do on its official GitHub repo. Some examples are simplified from the examples on their official website.


Full visibility into production React apps

Debugging React applications can be difficult, especially when users experience issues that are difficult to reproduce. If you’re interested in monitoring and tracking Redux state, automatically surfacing JavaScript errors, and tracking slow network requests and component load time, try LogRocket.

Alt Text

LogRocket is like a DVR for web apps, recording literally everything that happens on your React app. Instead of guessing why problems happen, you can aggregate and report on what state your application was in when an issue occurred. LogRocket also monitors your app's performance, reporting with metrics like client CPU load, client memory usage, and more.

The LogRocket Redux middleware package adds an extra layer of visibility into your user sessions. LogRocket logs all actions and state from your Redux stores.

Modernize how you debug your React apps — start monitoring for free.


The post Building and styling tables with react-table v7 appeared first on LogRocket Blog.

💖 💪 🙅 🚩
bnevilleoneill
Brian Neville-O'Neill

Posted on April 29, 2020

Join Our Newsletter. No Spam, Only the good stuff.

Sign up to receive the latest update from our blog.

Related