Tables are used to present data in a tabular form and are great for looking at individual values as opposed to trends in data. Simply put, a table consists of an ordered arrangement of data into rows and columns (think of an Excel spreadsheet). The elements of a table may be grouped, segmented or arranged in many different ways. They can even be nested recursively. Tables are widely used in communication, research and data analysis.
As a data analytics company, one of the most complex and function-filled UI components in our application is a Table. This is because they have multiple use cases ranging from showing search results that may contain tens of thousands of results to simple tables for managing administrative objects like users and roles. While there is nothing inherently special about React Tables, they can be rather difficult to implement, especially if they are function-filled like some of the ones we create here.
Consider the complexity required for showing search results:
- Freezing the first column
- Flexible pagination as there can be thousands (or may be millions) of results and users can choose how many items to show per page
- Searching within the results
- Sorting results
- Resizing and dragging columns
This all must be available while results are still downloading and the table is updated continuously. On the other hand, showing all users within an organization requires a single table with potentially thousands of entries, which we need to scroll smoothly without pagination.
By the time we finally got to writing the list of features we needed, it became overwhelming.
Don’t believe me? Here is the list:
- Horizontal scrolling
- Resizable columns
- Draggable columns
- Hide/show columns
- Inline editing of table cells
- Components within table cells
- Fixed header
- Column freeze
- Implement the Sumo Logic design language
- Infinite/virtual scrolling to make scrolling thousands of rows smooth
- No lag and overall smooth user experience.
- If we are using a library, it should not have any open issues related to our requirements
The Table Analysis & Selection Process
Everybody on our UI dev team was excited to have the chance to implement this challenging component.
Then came the hammer of timelines — it had to be done in one month — so we decided to see if one of the available libraries could do the job for us. Here is the evaluation of the three best options we found:
|Feature||Ag-Grid(v17.1.0)||React Table(v6.8.2)||React Data Grid(v4.0.8)|
|Draggable columns||✅||❌(Needs a higher order component, or HOC, i.e. a wrapper over this)||✅|
|Inline editing of table cells||✅||✅||✅|
|Sub-components within table rows/cells||✅||✅||✅|
|Column freeze||✅||❌(Needs a HOC i.e. a wrapper over this)||✅|
|Support Sumo Logic styles||✅||✅||✅|
|Infinite scroll||✅||❌(Needs a HOC i.e. a wrapper over this)||✅|
|No open issues for our features||✅||✅||❌(Warnings when used with React 16 and issues in dragging columns)|
|Hide/show columns||✅||❌(but can be implemented using existing features)||❌(but can be implemented using existing features)|
|No lag/smooth user experience||✅||✅||❌(buggy infinite scroll and slow performance)|
|Virtual Scroll/Smooth scroll for thousands of rows||✅||❌(Needs a HOC i.e. a wrapper over this)||❌(No such feature)|
|Free to use||❌(but has all the above features in free version)||✅||✅|
Based on the above, Ag-Grid seems like it should have won the race loud and clear, right? But hold it right there. It’s 732kb in size without styles unzipped, and still 146kb after Gzipping.
Also, to use Ag-Grid with React requires another package (Ag-Grid-React), which itself is 9kb when unzipped and 3kB when Gzipped.
We eliminated React-Data-Grid from our list of choices due to its buggy infinite scroll and no support for React 16, triggering multiple warning when used with React Fibre (yes, I am going to get fancy and use this term). Furthermore, it’s perceived performance is poor and provides a bad user experience altogether when the number of rows is greater than 100. We compared the performances of Ag-Grid and React Table. Here is the comparison:
2.8GHz intel core i7, 16 GB 2133 MHz LPDDR3, 256GB SSD, macOS High Sierra
Difficult, difficult decisions! Ag-Grid smokes React Table in terms of its performance and it also has a feature list that smokes React Table. But man it is huge (the big elephant in the room)! Ag-Grid won fair and square as we decided to lazy load the library and cache it on a user’s browser.
Writing a Wrapper for Our Table
Next step was writing a wrapper for our Table component so we could replace the underlying library in the future with our own implementation, or a better library.
We needed to complete the following:
- Limit the exposed functionality of the underlying library to only the features our app needs
- Decide on a folder structure – it can be hard to organize things when we are designing a component independent of the underlying library. This is especially true for complex components like Table.
- Style the table row, column, header, cell or entire table per our design language
- Create a component API for container components
- Devise a standard format of data to be passed in
- Devise a standard format to pass column headers and options
- Add new/missing features or workarounds for existing bugs
- Write unit tests
- Implement internationalization support
- Write necessary utility functions
- Lazy load the library
These are all implementation details and can vary from one use case to another. We will outline our implementation for you in a second blog post, coming soon!
We evaluated three different libraries for a React Table that met our requirements: Ag-Grid, React Table and React-Data-Grid. React-Data-Grid did not support React 16 and had performance issues so was ruled out. We compared Ag-Grid and React Table and the former outperformed the latter by a significant margin. Despite its size we decided to separate out Ag-Grid on its own chunk and use it. Further, we added a wrapper over Ag-Grid to add our own styles apart from other functionalities.
We hope this has been useful for you and will help you arrive at a better decision on your own table component. Thanks for reading, and come back for part two to learn how we implemented the table.