React Final Table

A headless UI library for React tables, inspired by
react-table but with Typescript
support built in and a simpler API.

Features

  • Type safe
  • Global row filtering
  • Row selection
  • Custom column rendering
  • Column sorting
  • Data memoization for performance
  • Zero dependencies

Motivation

While there are a plethora of table libraries available for every framework,
most are opinionated about the UI. This is a minimal, type-safe headless UI
library that you can plugin to whatever frontend framework you're using, as long
as you're using React 16 and functional components.

Install

npm install react-final-table

Hooks

useTable

This is the main hook exposed by the library and should be your entrypoint for
any table functionality. Only columns and data are required as arguments:

const {
  headers,
  rows,
  selectRow,
  selectedRows
} = useTable(columns, data, {
  selectable?: boolean,
  filter?: (rows: RowType<T>[]) => RowType<T>[],
});

Basic example

import { useTable } from 'react-final-table';

const columns = [
  {
    name: 'firstName',
    label: 'First Name',
    render: ({ value }) => <h1>{value}</h1>, // optional
  },
  {
    name: 'lastName',
    label: 'Last Name',
  },
];

const data = [
  {
    firstName: 'Frodo',
    lastName: 'Baggins',
  },
  {
    firstName: 'Samwise',
    lastName: 'Gamgee',
  },
];

const MyTable = () => {
  const { headers, rows } = useTable(columns, data);

  return (
    <table>
      <thead>
        <tr>
          {headers.map((header, idx) => (
            <th key={idx}>{header.label}</th>
          ))}
        </tr>
      </thead>
      <tbody>
        {rows.map((row, idx) => (
          <tr key={idx}>
            {row.cells.map((cell, idx) => (
              <td key={idx}>{cell.render()}</td>
            ))}
          </tr>
        ))}
      </tbody>
    </table>
  );
};

Advanced Example

import React, { useMemo } from 'react';
import { useTable } from 'react-final-table';

const columns = [
  { name: 'id', hidden: true },
  {
    name: 'first_name',
    label: 'First Name',
    render: ({ value }: { value: string }) => <span>Sir {value}</span>,
  },
  {
    name: 'last_name',
    label: 'Last Name',
  },
];

const data = [
  {
    id: 1,
    first_name: 'Frodo',
    last_name: 'Baggins',
  },
  {
    id: 2,
    first_name: 'Samwise',
    last_name: 'Gamgee',
  },
];

function App() {
  const memoColumns = useMemo(() => columns, []);
  const memoData = useMemo(() => data, []);

  const { headers, rows, selectRow, selectedRows } = useTable(
    memoColumns,
    memoData,
    {
      selectable: true,
    }
  );

  return (
    <>
      <table>
        <thead>
          <tr>
            <th></th>
            {headers.map((header, idx) => (
              <th key={idx}>{header.label}</th>
            ))}
          </tr>
        </thead>
        <tbody>
          {rows.map((row, idx) => (
            <tr key={idx}>
              <td>
                <input
                  type="checkbox"
                  onChange={e => {
                    selectRow(row.id);
                  }}
                />
              </td>
              {row.cells.map((cell, idx) => (
                <td key={idx}>{cell.render()}</td>
              ))}
            </tr>
          ))}
        </tbody>
      </table>
      <pre>
        <code>{JSON.stringify(selectedRows, null, 2)}</code>
      </pre>
    </>
  );
}

export default App;

Test

npm run test

GitHub