react-infinite-table

React table with infinite rows and much more!

Downloads in past

Stats

StarsIssuesVersionUpdatedCreatedSize
react-infinite-table
14331.3.04 years ago4 years agoMinified + gzip package size for react-infinite-table in KB

Readme

React Infinite Table
NPM version NPM license npm bundle size npm bundle size NPM total downloads NPM monthly downloads
A browser-ready efficient scrolling table with fixed header and footer, and much more!

Check out the demo

Features

  • ✅ Render only the visibile rows
    ✅ Fixed header
  • ✅ Fixed footer
  • ✅ Fixed left column(s)
  • ✅ Column resize
  • ✅ Column order changes (by dragging)
  • ✅ Row selection (handling shift/modifier during click)
  • ✅ Multiple rows selection
⌘/Ctrl + Click: toggle selection ⇧ + Click: select all the rows between the previously clicked row and the currently clicked row.
  • 🔜 Rows with different heights / free height
  • 🎉 Uses the HTML <table> standard tags!
  • 🎉 No use of javascript to sync scroll between fixed columns/rows!

Getting started

Install react-infinite-table using npm.
npm install react-infinite-table --save

Import and use as follows:
import {Table} from 'react-infinite-table';

const rows = [
  { /* the row's data */ }, 
  //...
]

const columns = [
  { 
    cellRenderer: ({ columnIndex, column, rowData, rowIndex, className }) => {
      return (
        <td className={className}>
          {rowData.xxx}
        </td>
      )
    },
    headerRenderer: ({ columnIndex, column, className }) => {
      return (
        <th className={className}>
          {column.name}
        </th>
      )
    },
    footerRenderer: ({ columnIndex, column, className }) => {
      return (
        <td className={className}>
          {column.xxx}
        </td>
      )
    },
    width: 90,
    name: '...'
  },
  //...
]

<Table
  className='example-table'
  tableClassName='table table-bordered table-striped' // example using bootstrap
  height={200}
  rowHeight={30}
  rows={rows}
  columns={columns}
  headerCount={1}
  footerCount={1}
  fillTableWidth={true|false}
  rowIdKey='id'
  noRowsRenderer={() => 'No rows'}
  // keep the first column fixed:
  fixedColumnsCount={1}
  // row selection
  selectedRows={this.state.selectedRows}
  onSelectionChange={selectedRows => { this.setState({selectedRows}) }}
  canSelectMultipleRows={true|false}
  // infinite load:
  infiniteLoadBeginEdgeOffset={150}
  isInfiniteLoading={true|false}
  onInfiniteLoad={() => { /* fetch your data ... */ }}
  getLoadingSpinner={() => <div>Loading...</div>}
  // display from bottom upwards, like a Chat or Message Box 
  displayBottomUpwards={displayBottomUpwards}
  // allows column resize
  onColumnWidthChange={(columnIndex, width) => { /* update columns... */ }}
  // allows column reorder (by dragging)
  onColumnOrderChange={(fromIndex, toIndex) => { /* update columns... */ }}
/>

Similar packages

This package has been inspired by some existing library dealing with tables in react.
The initial code started from a "fork" of the react-infinite package, that implements a scrollable container with a lot of items by rendering only DOM nodes that the user is able to see (or might soon see).
The definition of the columns and the rendering of the cells is inspired by react-virtualized.
CSS is used to fix header, footer and some columns, using position: sticky. This has been inspired by ember-table.
Comparison of similar packages:
| Feature | react-infinite-table | react-virtualized | react-base-table | | -------------- |:---:|:---:|:---:| | HTML <table> standard tags | ✅ | ❌ | ❌ | | Fixed columns | ✅ | ❌ | ✅ | | Fixed header | ✅ | ✅ | ✅ | | Fixed footer | ✅ | ❌ | ❌ | | Column resize (by dragging) | ✅ | ❌ | ✅ | | Column reorder (by dragging) | ✅ | ❌ | ❌ | | Row selection | ✅ | ❌ | ✅ | | No use of javascript to sync scroll between fixed columns/rows | ✅ | ❌ | ❌ | | Size | npm bundle size
npm bundle size | npm bundle size
npm bundle size | npm bundle size
npm bundle size |

Dependencies

react-infinite-table has very few dependencies that are managed by npm.
The following peer dependencies must be specified by your project in order to avoid version conflicts: react, react-dom. NPM will not automatically install these for you but it will show you a warning message with instructions on how to install them.

Documentation

Table Prop Types

Table is the main component and it's responsible of rendering your table.
| Property | Type | Required? | Description | | :---------------- | :-------- | :-------: | :---------------- | | height | Number | ✓ | The height of the table. If you want the table to take the height of the container, you should have a look at the AutoSizer component | | overscanSize | Number | | How much you want to render above/below the visible bounds of the table (in pixels). Default: overscanSize: 500 | | rowHeight | Number | ✓ | A fixed row height (TODO: planning to allow undefined, and each row will auto size to fit content!) | | rows | Array | ✓ | An array of data, one object for each row. There are no required keys, but if you want to use the "row selection" feature, an unique "row id" will be needed. | | columns | Column | ✓ | One or more columns describing the data displayed in this table | | headerCount | Number | | Number of header rows in the <thead> section of the table. Default: headerCount: 1 | | footerCount | Number | | Number of footer rows in the <tfoot> section of the table. Default: footerCount: 0 | | fixedColumnsCount | Number | | Number of columns to keep fixed on the left of the table while scrolling horizontally. Default: fixedColumnsCount: 0 | | fillTableWidth | Boolean | | If fillTableWidth = true and the combined width of the columns is less than the table's width, the columns will grow in order to fill the table. Default: fillTableWidth: false | | noRowsRenderer | Function | | Callback used to render placeholder content when the number of rows is 0. Default: noRowsRenderer: undefined - the table will be empty. | | rowIdKey | String | | The key used to extract the id of a row from the row's data object. If present, it is used as a key for the table's row, otherwise the rowIndex is used. Needed if row selection is enabled. | | selectedRows | Object | | An object with the selected rows' ids as key, and true as value. Example: { 1: true, 2: true, 5: true } - the rows with ids 1, 2 and 5 will be selected. Default: selectedRows: undefined - no selected rows. | | canSelectMultipleRows | Boolean | | Whether or not multiple rows can be selected. If true, using ⌘/Ctrl + Click on a row, the row will be selected/deselected, and using ⇧ + Click will select all the rows between the previously selected row and the clicked row. Default: canSelectMultipleRows: false | | onSelectionChange | Function | | The callback to call when the selection changes. For example, use (selectionRows) => { this.setState({ selectionRows }) } to update your state to reflect the new selection. Default: onSelectionChange: undefined - Row selection is not allowed. | | infiniteLoadBeginEdgeOffset | Number | | How far from the bottom of the table we call the onInfiniteLoad function to fetch the new data. Default: infiniteLoadBeginEdgeOffset: undefined - Infinite load is not enabled. | | onInfiniteLoad | Function | | The callback used to start a fetch operation to get new rows. You should use this callback to start the async loading of new data and then update the rows props with the new rows. Default: () => {} | | getLoadingSpinner | Function | | Callback used to render a loading message/spinner during data loading. Default: () => <div /> | | isInfiniteLoading | Boolean | | Set to true if you want the show the loading spinner. Default: isInfiniteLoading: undefined | | displayBottomUpwards | Boolean | | Whether or not to show the rows starting at the bottom, like in a chat/messaging application. Default: displayBottomUpwards: false | | className | String | | A class to add to the table container <div>. Default: className: undefined | | tableClassName | String | | A class to add to the <table> node. Default: tableClassName: undefined | | style | Object | | Passthrough prop to the table container <div>. Default: style: undefined | | onColumnWidthChange | Function | | Callback called when a column's width has changed. You should update the Column definition accordingly. (columnIndex, width) => { /* update columns... */ }. Default: onColumnWidthChange: undefined - Column's can't be resized. | | onColumnOrderChange | Function | | Callback called when a column order position is changed from fromIndex to toIndex. You should update the Columns array accordingly. (fromIndex, toIndex) => { /* update columns... */ }. Default: onColumnOrderChange: undefined - Column's can't be reordered.
You can use the helper function reorderColumns. Learn more |

Column Prop Types

| Property | Type | Required? | Description | | :---------------- | :-------- | :-------: | :---------------- | | width | Number | ✓ | The width of the column in pixels. | | cellRenderer | Function | ✓ | Callback responsible for rendering a cell's contents. Learn more | | headerRenderer | Function | | Optional callback responsible for rendering a column's header column. Learn more | | footerRenderer | Function | | Optional callback responsible for rendering a column's footer column. Learn more |

cellRenderer

Callback responsible for rendering a cell's contents. It should implement the following signature:
({
  className: string,
  columnIndex: number,
  column: object,
  rowIndex: number,
  rowData: object
}) => {
  return <td className={className}>{/*...*/}</td>
}

You should return a <td> node (or a Component that renders a td) since this node will be rendered in the tbody > tr section of the table. You should always pass the className prop.

headerRenderer

Callback responsible for rendering a cell's header column. It should implement the following signature:
({
  className: string,
  columnIndex: number,
  column: object
}) => {
  return <th className={className}>{/*...*/}</th>
}

You should return a <th> node (or a Component that renders a th) since this node will be rendered in the thead > tr section of the table. You should always pass the className prop.

footerRenderer

Callback responsible for rendering a cell's footer column. It should implement the following signature:
({
  className: string,
  columnIndex: number,
  column: object
}) => {
  return <td className={className}>{/*...*/}</td>
}

You should return a <td> node (or a Component that renders a td) since this node will be rendered in the tfoot > tr section of the table. You should always pass the className prop.

Utils

reorderColumns

This function is provided as an helper to update the columns array after the columns have been reorderer.
import { reorderColumns } from 'react-infinite-table'

// ...

const newColumns = reorderColumns(oldColumns, fromIndex, toIndex)

Style

You should import the react-infinite-table/dist/style.css file, or if you use scss, you can import the styles as follows:
@import "~react-infinite-table/src/style.scss";

If you want to use the "row selection" feature, you should apply a style on the tr.tr-selected rows as follows:
.react-infinite-table tbody tr.tr-selected td {
  background-color: #007bff;
  border-color: #007bff;
  color: #ffffff;
}

If you use bootstrap, you need to apply the following fixes:
@import "~bootstrap/scss/bootstrap.scss";

// For "Striped" Tables
// by default, bootstrap apply the background color to the <tr>
// and it's a semi-transparent color.

// we need an opaque color
$table-accent-bg: #F2F2F2;

// and we need to apply the backgrounds on td, not on tr!
.table-striped {
  tbody tr:nth-of-type(#{$table-striped-order}) {
    background-color: unset;
  }
  tbody tr:nth-of-type(#{$table-striped-order}) td {
    background-color: $table-accent-bg;
  }
}

// For "Bordered" Tables
// we need to apply the table border on the "wrapper" and not on 
// the <table> so that it is not hidden when scrolling.
.react-infinite-table {
  .table-bordered {
    border: none;
  }
  .react-infinite-table-wrapper {
    border: $table-border-width solid $table-border-color;
  }
}

Development

You can run the demo:
npm start

and make changes to the code. The browser will automatically load the changes.

Contributions

Use GitHub issues for requests.
I welcome pull requests. Please ask first before embarking on any significant pull request, otherwise you risk spending a lot of time working on something that the project's developers might not want to merge into the project.

Changelog

Changes are tracked in the changelog.

License

react-infinite-table is available under the MIT License.