react-data-table-component

A simple, sortable, and flexible table library

Github stars Tracking Chart

Build Status npm version codecov Storybook

1. React Data Table Component

Creating yet another React table library came out of necessity while developing a web application for a growing startup. I discovered that while there are some great table libraries out there, some required heavy customization, were missing out of the box features such as built in sorting and pagination, or required understanding the atomic structure of html tables.

If you want to achieve balance with the force and want a simple but flexible table library give React Data Table Component a chance. If you require an Excel clone or heavy "enterprise" capabilities, then this is not the React table library you are looking for ?

1.1. Demo and Examples

React Data Table Component Demo

1.2. Key Features

  • Declarative Configuration
  • Built-in and configurable:
    • Sorting
    • Selectable Rows
    • Expandable Rows
    • Pagination
  • Themeable/Customizable
  • Accessibility
  • Responsive (via x-scroll/flex)

1.3. Requirements

React Data Table Component requires the following be installed in your project:

  • React 16.8.0+
  • styled-components 3.2.3+, 4.0.0+, 5.0.0+

1.4. Installation

React Data Table requires the wonderful styled-components library. If you've already installed styled-components there is no need to install it again.

npm install react-data-table-component styled-components

or

yarn add react-data-table-component styled-components

1.5. Logging Issues and Contributions

Please use the github issue templates feature for logging issues or feature proposals. Including a codesandbox and providing clear details on the feature/issue will elicit a much quicker response ?

1.6. API and Usage

1.6.1. Columns

Nothing new here - we are using an array of object literals and properties to describle the columns:, Property, Type, Required, Example, ----------, --------, ----------, ---------------------------------------------------------------------------------------------------------------, name, string, component or number, no, the display name of our Column e.g. 'Name', selector, string or function, no, a data set property in dot notation. e.g. property1.nested1.nested2 property1.items[0].nested2 or as a function e.g. row => row.timestamp. A selector is required anytime you want to display data but can be ommitted if your column does not require showing data (e.g. an actions column), sortable, bool, no, if the column is sortable. note that selector is required for the column to sort, format, func, no, apply formatting to the selector e.g. row => moment(row.timestamp).format('lll') without changing the actual selector value, cell, func, no, for ultimate control use cell to render your own custom component! e.g row => <h2>{row.title}</h2> **negates format*-, grow, number, no, flex-grow of the column. This is useful if you want a column to take up more width than its relatives (without having to set widths explicitly). this will be affected by other columns where you have explicitly set widths, width, string, no, give the column a fixed width, minWidth, string, no, give the column a minWidth, maxWidth, string, no, give the column a maxWidth, right, bool, no, right aligns the content in the cell. useful for numbers, center, bool, no, center aligns the content in the cell, compact, bool, no, sets cell padding to 0, ignoreRowClick, bool, no, prevents the onRowClicked and onRowDoubleClicked event from being passed on the specific TableCell column. This is *really- useful for a menu or button where you do not want the onRowClicked triggered, such as when using onRowClicked for navigation or routing, button, bool, no, this is like ignoreRowClick except it will apply additional styling for button placement. you do not need to set ignoreRowClick when using button, wrap, bool, no, whether the cell content should be allowed to wrap., allowOverflow, bool, no, allows content in the cell to overflow. useful for menus/layovers that do not rely on "smart" positioning, hide, integer or string preset (sm, md, lg), no, specify a screen size (breakpoint) as an integer (in pixels) that hides the column when resizing the browser window. You can also use the preset values of: sm (small), md(medium), and lg(large), style, object, no, allows you to customize the css of the cell using css-in-js style objects, conditionalCellStyles, array, no, allows an array of conditional style objects to conditionally apply css styles to a cell, #### 1.6.1.1. column.hide media presets

When the breakpoint is reached the column will be hidden. These are the built-in media breakpoint presets when hiding columns, Value, Breakpoint, Description, -------, -----------, -------------------------, sm, 599px, small (phones), md, 959px, medium(landscape tablets), lg, 1280px, large(laptops/desktops), ### 1.6.2. DataTable Properties

You can easily toggle to dark mode by setting theme="dark"

1.6.2.9.2. Defining Your Own Theme Using createTheme

You can create your very own theme using the createTheme helper. Note that createTheme inherits from the default theme. Note that this theme will now be available to all DataTables across your project so you may want to define your custom themes in a seperate file.

Refer to themes.js for properties you can use to create your own color theme.

import DataTable, { createTheme } from 'react-data-table-component';

createTheme('solarized', {
  text: {
    primary: '#268bd2',
    secondary: '#2aa198',
  },
  background: {
    default: '#002b36',
  },
  context: {
    background: '#cb4b16',
    text: '#FFFFFF',
  },
  divider: {
    default: '#073642',
  },
  action: {
    button: 'rgba(0,0,0,.54)',
    hover: 'rgba(0,0,0,.08)',
    disabled: 'rgba(0,0,0,.12)',
  },
});

const MyComponent = () => (
  <DataTable
    title="Arnold Movies"
    columns={columns}
    theme="solarized"
  />
);
1.6.2.9.3. Overidding Styling using css-in-js

For more advanced use cases you can override or replace the default styling using the customStyles prop and passing in css-in-js. Internally, this just deep merges your customStyles with the default styling. Disclaimer: you're on your own here since you will have the power to not only cusotmize but break RDT. This is the sky's the limit escape hatch feature.

Let's apply a simple customStyles to override the default row height and change the cell padding:

import DataTable from 'react-data-table-component';

const customStyles = {
  rows: {
    style: {
      minHeight: '72px', // override the row height
    }
  },
  headCells: {
    style: {
      paddingLeft: '8px', // override the cell padding for head cells
      paddingRight: '8px',
    },
  },
  cells: {
    style: {
      paddingLeft: '8px', // override the cell padding for data cells
      paddingRight: '8px',
    },
  },
};

const MyComponent = () => (
  <DataTable
    title="Arnold Movies"
    columns={columns}
    customStyles={customStyles}
  />
);

View styles.js for a detailed catalog of RDT styles that you can override or extend using css-in-js objects.

1.6.2.10. Conditional Row Styling, Property, Type, Required, Default, Description, --------------------------, ---------------------, ----------, ---------, -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------, conditionalRowStyles, array, no, [], Allows an array of conditional style objects

1.6.2.10.1. Example

The following will style the background color of a row to green and set a hover effect when the expression row => row.calories < 300 evaluates to true, Property, Type, Required, Example

...
const conditionalRowStyles = [
  {
    when: row => row.calories < 300,
    style: {
      backgroundColor: 'green',
      color: 'white',
      '&:hover': {
        cursor: 'pointer',
      },
    },
  },
];

const MyTable = () => (
  <DataTable
    title="Desserts"
    columns={columns}
    data={data}
    conditionalRowStyles={conditionalRowStyles}
  />
);

1.6.2.11. Conditional Style Object, Property, Type, Required, Description, ----------, ----------, -----------, -------------------------------------------------------------------------------------------------------------------------, when, function, yes, when accepts a callback that gives you access to your row data. The when callback must return a boolean to determine if the style will be applied. e.g. row => row.status === 'completed' will apply the style when the row.status field is completed, style, object, yes, css-in-js style object, ## 1.7. Basic Table

The following declarative structure creates a sortable table of Arnold movie titles:

import DataTable from 'react-data-table-component';

const data = [{ id: 1, title: 'Conan the Barbarian', year: '1982' } ...];
const columns = [
  {
    name: 'Title',
    selector: 'title',
    sortable: true,
  },
  {
    name: 'Year',
    selector: 'year',
    sortable: true,
    right: true,
  },
];

class MyComponent extends Component {
  render() {
    return (
      <DataTable
        title="Arnold Movies"
        columns={columns}
        data={data}
      />
    )
  }
};

1.8. Selectable Rows

Let's make our rows selectable so we can access the selected results

...

const handleChange = (state) => {
  // You can use setState or dispatch with something like Redux so we can use the retrieved data
  console.log('Selected Rows: ', state.selectedRows);
};

class MyComponent extends Component {
  render() {
      return (
        <DataTable
          title="Arnold Movies"
          columns={columns}
          data={data}
          selectableRows // add for checkbox selection
          Clicked
          Selected={handleChange}
        />
    )
  }
};

1.8.1. Clearing Selected Rows

We need some hook to trigger all the selectedRows to clear. If you were building your own table component, you would manage the selected rows state in some parent component, however, in our case, since we to keep row management within React Data Table, a clearSelectedRows prop is provided so you can pass a toggled state.

It will be up to you to make sure you do not pass the same state twice. For example, if you set clearSelectedRows={true} twice, on the second update/trigger, none the rows will not be cleared.

...
// set the initial state
state = { toggledClearRows: false }
...

const handleChange = (state) => {
  // You can use setState or dispatch with something like Redux so we can use the retrieved data
  console.log('Selected Rows: ', state.selectedRows);
};

// Toggle the state so React Table Table changes to `clearSelectedRows` are triggered
const handleClearRows = () => {
  this.setState({ toggledClearRows: !this.state.toggledClearRows})
}

class MyComponent extends Component {
  render() {
    return (
      <DataTable
        title="Arnold Movies"
        columns={columns}
        data={data}
        selectableRows // add for checkbox selection
        onSelectedRowsChange={handleChange}
        clearSelectedRows={this.state.toggledClearRows}
      />
    )
  }
};

1.8.2. Overriding with Ui Component Library

Don't like those ugly html checkboxes? Let's override them with some Material Ui sexiness. While we are at it we will also override the sortIcon:

...
import Checkbox from '@mataerial-ui/core/Checkbox';
import ArrowDownward from '@material-ui/icons/ArrowDownward';

const sortIcon = <ArrowDownward />;
...

const MyComponent = () => (
  <DataTable
    title="Arnold Movies"
    columns={columns}
    data={data}
    selectableRows
    selectableRowsComponent={Checkbox} // Pass the function only
    selectableRowsComponentProps={{ inkDisabled: true }} // optionally, pass Material Ui supported props down to our custom checkbox
    sortIcon={sortIcon} // use a material icon for our sort icon. rdt will rotate the icon 180 degrees for you
    onSelectedRowsChange={handleChange}
  />
);

1.9. Using Custom Checkboxes and Indeterminate State

Sometimes UI Library checkbox components have their own way of handling indeterminate state. We don't want React Data Table hard coded to a specific ui lib or custom component, so instead a "hook" is provided to allow you to pass a function that will be resolved by React Data Table's internal Checkbox for use with indeterminate functionality.

Example Usage:


import Checkbox from '@mataerial-ui/core/Checkbox';

...

/*
  In this example, the Material Ui ui lib determines its own indeterminate state via the `indeterminate` property.
  Let's override it using selectableRowsComponentProps`
*/
const selectProps = { indeterminate: isIndeterminate => isIndeterminate };

const MyComponent = () => (
  <DataTable
    title="Arnold Movies"
    columns={columns}
    data={data}
    selectableRows
    selectableRowsComponent={Checkbox} // Pass the function only
    selectableRowsComponentProps={selectProps}
  />
);

*Note- This is currently only supported for indeterminate state, but may be expanded in the future if there is a demand

1.10. Custom Cells

Let's give our Movie list a summary, but in the same cell as Name:

....

const data = [{ id: 1, title: 'Conan the Barbarian', summary: 'Orphaned boy Conan is enslaved after his village is destroyed...',  year: '1982' } ...];
const columns = [
  {
    name: 'Title',
    sortable: true,
    cell: row => <div><div style={{ fontWeight: bold }}>{row.title}</div>{row.summary}</div>,
  },
  {
    name: 'Year',
    selector: 'year',
    sortable: true,
    right: true,
  },
];

...

class MyComponent extends Component {
  render() {
    return (
      <DataTable
        title="Arnold Movies"
        columns={columns}
        data={data}
        selectableRows
        selectableRowsComponent={Checkbox}
        selectableRowsComponentProps={{ inkDisabled: true }}
        sortIcon={<FontIcon>arrow_downward</FontIcon>}
        onSelectedRowsChange={handleChange}
      />
    )
  }
};

1.11. Expandable Rows

Let's make our rows expandable so we can view more details:

...

const data = [{ id: 1, title: 'Conan the Barbarian', summary: 'Orphaned boy Conan is enslaved after his village is destroyed...',  year: '1982', image: 'http://conan.image.png' } ...];
const columns = [
  {
    name: 'Title',
    sortable: true,
    cell: row => <div><div style={{ fontWeight: 700 }}>{row.title}</div>{row.summary}</div>,
  },
  {
    name: 'Year',
    selector: 'year',
    sortable: true,
    right: true,
  },
];

...

// The row data is composed into your custom expandable component via the data prop
const ExpanableComponent = ({ data }) => <img src={data.image} />;

class MyComponent extends Component {
  render() {
    return (
      <DataTable
        title="Arnold Movies"
        columns={columns}
        data={data}
        selectableRows
        selectableRowsComponent={Checkbox}
        selectableRowsComponentProps={{ inkDisabled: true }}
        sortIcon={<FontIcon>arrow_downward</FontIcon>}
        onSelectedRowsChange={handleChange}
        expandableRows
        expandableRowsComponent={<ExpanableComponent />}
      />
    )
  }
};

But in some cases we don't have more details to show:

...

const data = [{ id: 1, title: 'Conan the Barbarian', summary: 'Orphaned boy Conan is enslaved after his village is destroyed...',  year: '1982', expanderDisabled: true, image: 'http://conan.image.png' } ...];
const columns = [
  {
    name: 'Title',
    sortable: true,
    cell: row => <div><div style={{ fontWeight: 700 }}>{row.title}</div>{row.summary}</div>,
  },
  {
    name: 'Year',
    selector: 'year',
    sortable: true,
    right: true,
  },
];

...

// The row data is composed into your custom expandable component via the data prop
const ExpanableComponent = ({ data }) => <img src={data.image} />;

class MyComponent extends Component {
  render() {
    return (
      <DataTable
        title="Arnold Movies"
        columns={columns}
        data={data}
        selectableRows
        selectableRowsComponent={Checkbox}
        selectableRowsComponentProps={{ inkDisabled: true }}
        sortIcon={<FontIcon>arrow_downward</FontIcon>}
        onSelectedRowsChange={handleChange}
        expandableRows
        expandableRowDisabled={row => row.disabled}
        expandableRowsComponent={<ExpanableComponent />}
      />
    )
  }
};

1.12. UI Library Integration

React Data Table Component makes it easy to incorporate ui components from other libraries for overriding things like the sort icon, select checkbox.

1.13. Optimizing for Performance and Caveats

Pre-optimizaton can be the root of all evil, however, there are some best practices you can adhere to that will ensure React Data Table (RDT) is giving you the performance that you expect.

1.13.1. Passing non-primitive props (objects, arrays and functions)

While RDT has internal optimizations to try and prevent re-renders on deeper internal components, it's up to you to make sure that you understand how React manages rendering when props/state change as well as how JavaScript determines equality for non-primitives. As a general rule, or if you are experiencing performance issues you should ensure that any non-primitive props passed into RDT are not re-created on every render cycyle. This is ever important when you have larger data sets or you are passing complex components and columns to DataTable.

1.13.1.1. Optimizing Class Components

You can typically achieve this by moving props such as objects, arrays, functions or other React components that you pass to RDT outside of the render method. For cases where you need to memoize memoize-one is a great library.

1.13.1.1.1. Examples of Optimizations

The following component will cause RDT to fully re-render every time onSelectedRowsChange is triggered. Why? Because when setState is called it triggers myComponent to re-render which by design triggers a re-render on all child components i.e. DataTable. But luckily for you React optimally handles this decision on when and how to re-render DataTable and a full re-render should not occur as long as DataTable props are the same.

However, in the example below columns changes on every re-render because it's being re-created. This is due to referential equality checking, simply: columns[] !== columns[]. In other words, while both instances of columns contain the same elements, they are "different" arrays.

Bad

...
import React, { Component } from 'react';
import DataTable from 'react-data-table';

class MyComponent extends Component {
  updateState = state => {
    this.setState({ selectedRows: state.selectedRows }); // triggers MyComponent to re-render with new state
  }

  render () { // by design runs on every setState trigger
    // upon re-render columns array is recreated and thus causes DataTable to re-render
    const columns = [....];

    return (
      <DataTable
        data={data}
        columns={columns}
        onSelectedRowsChange={this.updateState}
        selectableRows
      />
    )
  }
}

A "solution" could be to declare any field that is a non primitive field outside of the render function so that it does not get recreated on every re-render cycle:

Good

...
import React, { Component } from 'react';
import DataTable from 'react-data-table';

const columns = [....]; // is only created once

class MyComponent extends Component {
  updateState = state => {
    this.setState({ selectedRows: state.selectedRows });
  }

  render () {

    return (
      <DataTable
        data={data}
        columns={columns}
        onSelectedRowsChange={this.updateState}
        selectableRows
      />
    )
  }
}

But that only works if you don't need to pass component props/methods to the column object. For example what if you want to attach an event handler to a button in the row using column.cell?

const columns = [;
  {
    cell: () => <Button raised primary onClick={this.handleAction}>Action</Button>,
    ignoreRowClick: true,
    allowOverflow: true,
    button: true,
  },
  ...
];

So how do we attach event handlers to our columns without having to place it in the render method and dealing with unnecessary re-renders?

  1. Create a columns function and pass the arguments needed
  2. Memoize the columns function

This way, when React checks if columns has changed columns will instead be the cached result (remember referential equality), thus no unnecessary re-render.

Got it? Let's try this again with the optimal solution using the wonderful memoization library memoize-one:

import React, { Component } from 'react';
import memoize from 'memoize-one';
import DataTable from 'react-data-table';

const columns = memoize(handleAction => [
  ...
  {
    cell: () => <Button raised primary onClick={handleAction}>Action</Button>,
    ignoreRowClick: true,
    allowOverflow: true,
    button: true,
  },
  ...
]);

class MyComponent extends Component {
  updateState = state => {
    this.setState({ selectedRows: state.selectedRows });
  }

  render () {
    return (
      <DataTable
        data={data}
        columns={columns(this.updateState)}
        onSelectedRowsChange={this.updateState}
        selectableRows
      />
    );
  }
}

Notice that this.updateState does not require memoization. That's because this.updateState is defined as a class method and therefore only created once. This however, is a different matter with functional components.

1.13.1.2. Optimizing Functional Components

If you're building functional components in React 16.8+ you get access to React Hooks such as useMemo and useCallback. In this example, simply wrap columns in a useMemo callback and your updateState into useCallback:

import React, { useState, useMemo } from 'react';
import DataTable from 'react-data-table';

const MyComponentHook = () => {
  const [thing, setThing] = useState();
  const handleAction = value => setThing(value);
  // unlike class methods updateState will be re-created on each render pass, therefore, make sure that callbacks passed to onSelectedRowsChange are memoized using useCallback
  const updateState = useCallback(state => console.log(state));
  const columns = useMemo(() => [
    ...
    {
      cell: () => <Button raised primary onClick={handleAction}>Action</Button>,
      ignoreRowClick: true,
      allowOverflow: true,
      button: true,
    },
    ...
  ]);

  return (
    <DataTable
      data={data}
      columns={columns}
      onSelectedRowsChange={updateState}
      selectableRows
    />
  );
}

1.14. CSS Overrides

If you would like to customize the layout components of React Data Table using styled-components (e.g. styled(DataTable)), or your favorite CSS, SCSS, LESS, etc.. pre-processor you may use the following classNames:

  • rdt_Table
  • rdt_TableRow
  • rdt_TableCol
  • rdt_TableCol_Sortable
  • rdt_TableCell
  • rdt_TableHeader
  • rdt_TableFooter
  • rdt_TableHead
  • rdt_TableHeadRow
  • rdt_TableBody
  • rdt_ExpanderRow

2. Development

2.1. Setup

Install the latest Node JS LTS and Yarn and simply run yarn or yarn install command in the root and stories directory.

It is advised to run the script whenever NPM packages are installed.

2.2. Local development

During development:

# watch and build new source changes
yarn start
# or serve *.stories.js files and manually test on the Storybook app
yarn storybook

2.3. Including NPM packages

This project uses two package.json structure.**

2.3.1. Library dependencies -- <root_dir>/package.**json

yarn add [package-name] --dev # for dev tools
yarn add [package-name] # for app

2.3.2. Storybook dependencies -- <root_dir>/stories/package.json

cd stories/
yarn add [package-name]

2.4. Lint

yarn lint # runs linter to detect any style issues (css & js)
yarn lint:css # lint only css
yarn lint:js # lint only js
yarn lint:js --fix # tries to fix js lint issues

2.5. Test

yarn test:tdd # runs functional/unit tests using Jest with watcher
yarn test # runs functional/unit tests using Jest
yarn test --coverage # with coverage

2.6. Build

yarn build # builds sources at src/

Main metrics

Overview
Name With Ownerjbetancur/react-data-table-component
Primary LanguageTypeScript
Program languageJavaScript (Language Count: 4)
Platform
License:Apache License 2.0
所有者活动
Created At2018-03-16 04:31:39
Pushed At2025-06-03 06:18:12
Last Commit At2025-02-24 21:24:11
Release Count154
Last Release Namev7.7.0 (Posted on )
First Release Namev0.8.0 (Posted on )
用户参与
Stargazers Count2.1k
Watchers Count32
Fork Count420
Commits Count0.9k
Has Issues Enabled
Issues Count811
Issue Open Count76
Pull Requests Count348
Pull Requests Open Count12
Pull Requests Close Count111
项目设置
Has Wiki Enabled
Is Archived
Is Fork
Is Locked
Is Mirror
Is Private