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 already available, most required heavy customization or lacked basic features such as built in sorting and pagination, and in some cases required a restrictive license.
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 and need to pivot large data sets then this is not the React table library you are looking for 👋
Table of Contents
Demo and Examples
React Data Table Component Demo
Key Features
selectedRows
Requirements
React Data Table Component requires the following be installed in your project:
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
yarn add react-data-table-component styled-components
Logging Issues and ContributionsPlease use the github issue templates feature for logging issues or feature proposals. Including a codesanbox and providing clear details on the feature/issue will elicit a much quicker response 😉
API and Usage
ColumnsNothing new here - we are using an array of object literals and properties to describle the columns:
Property
Required
Example
for ultimate control use cell
to render your own custom component! e.g row => <h2>{row.title}</h2>
Negates format
number
flex-grow of the column. the 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
give the column a fixed width
minWidth
string
give the column a minWidth
maxWidth
string
give the column a maxWidth
right
right aligns the content in the cell. useful for numbers
center
center aligns the content in the cell
compact
reduces the padding in the cell by 50%
button
applies additional styling when using a button
whether the cell content should be allowed to wrap.
allowOverflow
allows content in the cell to overflow. useful for menus/layovers that do not rely on "smart" positioning
ignoreRowClick
prevents the onRowClicked
event from being passed on a specific TableCell column. This is really useful for a menu or button where you do not want the onRowClicked
triggered
integer or string preset (sm
, md
, lg
)
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)
column.hide media presetsWhen the breakpoint is reached the column will be hidden. These are the built-in media breakpoint presets when hiding columns
Value
Breakpoint
Description
it is highly recommended that your data has a unique identifier (keyField). The default keyField
is id
. If you need to override this value then see keyField
DataTable Properties.
keyField
string
your data should have a unique identifier. By default, React Data Table looks for an id
property for each item in your data. You must match keyField
to your identifier key, especially if you want to manage row state at a later time or use the expander feature. If a unique id
is not present, React Data Table will use the row index (not recommended) as the key value
striped
false
stripe color the odd rows
highlightOnHover
false
if rows are to be highlighted on hover
pointerOnHover
false
if rows show a point icon on hover
noDataComponent
string or component
A custom component to display when there are no records to display
className
string
override the className on the Table wrapper
style
object
override the style on the Table wrapper
responsive
makes the table horizontally scrollable on smaller screen widths
customTheme
object
Override the default theme, by overriding specifc props. Your changes will be merged. See Theming for more information
disabled
false
disables the Table section
onRowClicked
callback to access the row data,index on row click
overflowY
false
if a table is responsive, items such as layovers/menus/dropdowns will be clipped on the last row(s) due to to overflow-x-y behavior - setting this value ensures there is invisible space below the table to prevent "clipping". However, if possible, the correct approach is to use menus/layovers/dropdowns that support smart positioning. If used, the table parent element must have a fixed height
or height: 100%
.
overflowYOffset
string
250px
used with overflowY to "fine tune" the offset
Property
Required
Default
Description
false
toggling this property clears the selectedRows. If you use redux or react state you need to make sure that you pass a toggled value or the component will not update. See Progress IndicatorClearing Selected Rows
onRowSelected
callback to access the row selected state ({ allSelected, selectedCount, selectedRows }). It's highly recommended that you memoize any handlers that you pass to onRowSelected
to prevent DataTable
from unnescessary re-renders
selectableRowsComponent
Override the default checkbox component - must be passed as a function (e.g. Checkbox
not <Checkbox />
)
selectableRowsComponentProps
object
Additional props you want to pass to selectableRowsComponent
. See Advanced Selectable Component Options to learn how you can override indeterminate state
Property
Required
Default
Description
false
Whether to make a row expandable, if true it requires an Row ExpanderexpandableRowsComponent
. It is highly recommended your data set have a unique identifier defined as the keyField
for row expansion to work properly.
expandableIcon
object
default expander icons
you may pass in your own custom icons using the expandableIcon: { collapsed: <svg>...</svg>, expanded: <svg>...</svg>
expandableDisabledField
string
React Data Table looks for this property in each item from your data and checks if that item can be expanded or not. You must set a bool value in the expandableDisabledField
of your data if you want to use this feature.
defaultExpandedField
string
React Data Table looks for this property in each item from your data and checks if that item should be expanded on initial render. You must set a bool value in the defaultExpandedField
field of your data if you want to use this feature.
expandableRowsComponent
string or component
A custom component to display in the expanded row. It will have the data
prop composed so that you may access the row data
Property
Required
Default
Description
Setting this ensures the table data is presorted before it renders and the field(selector) is focused
defaultSortAsc
set this to false if you want the table data to be sorted in DESC order
sortIcon
component
Override the default sort icon - the icon must be a font or svg icon and it should be a "downward" icon since animation will be handled by React Data Table
onSort
callback to access the sort state when a column is clicked. returns ( Sortingcolumn, sortDirection, event)
sortFunction
pass in your own custom sort function e.g. `(rows, field, direction) => {...yourSortLogicHere}. you must return an array
Property
Required
Default
Description
false
enable pagination with defaults. by default the total record set will be sliced depending on the page, rows per page. if you wish to use server side pagination then use the PaginationpaginationServer
property
paginationServer
false
changes the default pagination to work with server side pagination
paginationDefaultPage
number
the default page to use when the table initially loads
paginationTotalRows
number
allows you to provide the total row count for your table as represented by your API when performing server side pagination. if this property is not provided then react-data-table will use data.length
paginationPerPage
number
the default rows per page to use when the table initially loads
paginationRowsPerPageOptions
number
[10, 15, 20, 25, 30]
row page dropdown selection options
onChangePage
callback when paged that returns onChangePage(page, totalRows)
onChangeRowsPerPage
callback when rows per page is changed returns onChangeRowsPerPage(currentRowsPerPage, currentPage)
paginationComponent
Pagination
a component that overrides the default paginator component
paginationComponentOptions
object
See Description
overridable options for the built in pagination component. If you are developing a custom pagination component you can use paginationComponentOptions
to pass in your own custom props. Defaults to: { rowsPerPageText: 'Rows per page:', rangeSeparatorText: 'of' }
paginationIconFirstPage
a component that overrides the first page icon for the pagination
paginationIconLastPage
a component that overrides the last page icon for the pagination
paginationIconNext
a component that overrides the next page icon for the pagination
paginationIconPrevious
a component that overrides the previous page icon for the pagination
Property
Required
Default
Description
100vh
in order for fixedHeader to work this property allows you to set a static height to the TabelBody. height must be a fixed value
subHeader
component or array of components
false
show a subheader between the table and table header
subHeaderAlign
string
right
align the subheader content (left, right, center)
subHeaderWrap
whether the subheader content should wrap
subHeaderComponent
component or array of components
a component you want to render
Header
Advanced Selectable Component OptionsSometimes 3rd party checkbox components have their own way of handling indeterminate state. We don't want React Data Table hardcoded 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:
const Checkbox from 'react-md'; ... /* In this example, the react-md ui lib determines its own indeterminate state via the `uncheckedIcon` property. Let's override it. React Data Table is made aware if a checkbox is indetermite or not becuase internally we can resolve this as `yourfunction(checkboxawareindeterminatestate)`*/ const handleIndeterminate = isIndeterminate ? <FontIcon>indeterminate_check_box</FontIcon> : <FontIcon>check_box_outline_blank</FontIcon>; const MyComponent = <DataTable title="Arnold Movies" columns=columns data=data selectableRows selectableRowsComponent=Checkbox // Pass the function only selectableRowsComponentProps= uncheckedIcon: handleIndeterminate />;
Note This is currently only supported for indeterminate state, but may be expanded in the future if there is a demand
Basic TableThe following declarative structure creates a sortable table of Arnold movie titles:
; 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 ; { return <DataTable title="Arnold Movies" columns=columns data=data /> });
Selectable RowsLet's make our rows selectable so we can access the selected results
... const handleChange = { // You can use setState or dispatch with something like Redux so we can use the retrieved data console;}; { return <DataTable title="Arnold Movies" columns=columns data=data selectableRows // add for checkbox selection onRowSelected=handleChange /> });
Clearing Selected RowsWe 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 statestate = toggledClearRows: false ... const handleChange = { // You can use setState or dispatch with something like Redux so we can use the retrieved data console;}; // Toggle the state so React Table Table changes to `clearSelectedRows` are triggeredconst handleClearRows = { this} { return <DataTable title="Arnold Movies"
columns=columns data=data selectableRows // add for checkbox selection onRowSelected=handleChange clearSelectedRows=thisstatetoggledClearRows /> });
Overriding with a 3rd Party Ui Component LibraryDon't like those ugly html checkboxes? Let's override them with some react-md sexyiness. While we are at it we will also override the sortIcon
:
...;... { return title="Arnold Movies" columns=columns data=data selectableRows selectableRowsComponent=Checkbox // Pass the function only selectableRowsComponentProps= inkDisabled: true // optionally, pass react-md supported props down to our custom checkbox sortIcon=<FontIcon>arrow_downward</FontIcon> // use a material icon for our sort icon. rdt will rotate the icon 180 degrees onRowSelected=handleChange /> });
Custom CellsLet'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 <div><div style= fontWeight: bold >rowtitle</div>rowsummary</div> name: 'Year' selector: 'year' sortable: true right: true ; ... {
return <DataTable title="Arnold Movies" columns=columns data=data selectableRows selectableRowsComponent=Checkbox selectableRowsComponentProps= inkDisabled: true sortIcon=<FontIcon>arrow_downward</FontIcon> onRowSelected=handleChange /> });
Expandable RowsLet'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 <div><div style= fontWeight: 700 >rowtitle</div>rowsummary</div> name: 'Year' selector: 'year' sortable: true right: true ; ... // The row data is composed into your custom expandable component via the data propconst ExpanableComponent = <img src=dataimage />; { return <DataTable title="Arnold Movies" columns=columns data=data selectableRows selectableRowsComponent=Checkbox selectableRowsComponentProps= inkDisabled: true sortIcon=<FontIcon>arrow_downward</FontIcon> onRowSelected=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 <div><div style= fontWeight: 700 >rowtitle</div>rowsummary</div> name: 'Year' selector: 'year' sortable: true right: true ; ... // The row data is composed into your custom expandable component via the data propconst ExpanableComponent = <img src=dataimage />; { return <DataTable title="Arnold Movies" columns=columns data=data selectableRows selectableRowsComponent=Checkbox selectableRowsComponentProps= inkDisabled: true sortIcon=<FontIcon>arrow_downward</FontIcon> onRowSelected=handleChange expandableRows expandableDisabledField="expanderDisabled" expandableRowsComponent=<ExpanableComponent /> /> });
Optimizing for Performance and CaveatsPre-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.
Passing non-primitive props (objects, arrays and functions)While RDT has internal optimizations to try and prevent re-renders on deeper internal components, for peak performance 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 property that's passed into RDT is not re-created on every render cycyle. This is even more important when you have larger data sets or you are passing complex components and columns to DataTable
.
Optimizing Class ComponentsYou can typically achieve this by moving props such as objects, arrays, functions or other React compents that you pass to RDT outside of the render
method. Additionally, RDT provides you with a memoize
helper for cases where you are using a function to generate those values.
Examples of OptimizationsThe following component will cause RDT to fully re-render everytime onRowSelected
is triggered. Why? Becuase 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 descision 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 becuase 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.
...
;; { this; // triggers MyComponet to re-render with new state } { // 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 onRowSelected=thisupdateState 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:
...;; const columns = ...; // is only created once { this; } { return <DataTable data=data columns=columns onRowSelected=thisupdateState 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 = ;
<Button raised primary onClick=thishandleAction>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 unnescessary re-renders?
Create a columns
function and pass the arguments needed
Memoize the columns
function
This way, when React checks if columns
has changes columns
will instead be cached result (remember referential equality), thus no unnessesary re-render.
Got it? Let's try this again with the optimal solution:
;; const onRowSelected = ; { this; } { return <DataTable data=data columns= onRowSelected=thisupdateState 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 function components.
Optimizing Functional ComponentsIf you're building functional components 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
:
;; const MyComponentHook = { const thing setThing = ;
const handleAction = ; // unklike class methods updateState will be re-created on each render pass, therefore, make sure that callbacks passed to onRowSelected are memoized using useCallback const updateState = ; const columns = ; return <DataTable data=data columns=columns onRowSelected=updateState selectableRows /> ;}
ThemingYou can override or replace the default theme using the customTheme
prop. Internally, this just deep merges your theme with the default theme.
For Example:
// Override the row default heightconst mySweetTheme = rows: height: '64px' { return <DataTable title="Arnold Movies" columns=columns customTheme=mySweetTheme /> });
Refer to Default Theme for reference and avaiilable properties to override
CSS OverridesIf 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.. preprocesor 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
Development
SetupInstall 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.
Local developmentDuring development,
# watch and build new source changes yarn start# or serve *.stories.js files and manually test on the Storybook app yarn storybook
Including NPM packagesThis project uses two package.json structure.
Library dependencies -- <root_dir>/package.jsonyarn add [package-name] --dev # for dev tools yarn add [package-name] # for app
Storybook dependencies -- <root_dir>/stories/package.jsoncd stories/yarn add [package-name]
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
yarn test # runs functional/unit tests using Jest yarn test --coverage # with coverage
Build