Open-source project
rsuite/rsuite-table avatar
rsuite/rsuite-table

rsuite-table: a React table with fixed columns, tree rows and virtualisation

A React table component.

741 stars137 forksTypeScriptMIT

At a glance

What is it?
rsuite-table is a React table component from the rsuite family, built around a declarative Column and Cell model, with virtualised rendering, affixable headers and scrollbars, resizable columns, sorting, expandable and tree-shaped rows, and right to left support. It is distributed as a package with both CommonJS and ES module builds plus its own type declarations.
Who is it for?
rsuite-table fits a React team that needs a data grid with the awkward parts already handled, a sticky header, a fixed first column, a tree of rows, and a large dataset that needs windowing. It does not fit a team that needs a grid framework rather than a component, because the project is one component with a documented property list, and the styling is a less file you import yourself.
Can I use it commercially?
Yes. MIT is a permissive licence: you can use, modify and sell software built on it, as long as you keep its copyright and licence notices.
Is it still maintained?
Yes. The repository received new commits within the last day.
What is it written in?
Mainly TypeScript, according to GitHub's language statistics.

Answers come from the project's GitHub data, last synced on October 23, 2026, and from our analysis. They are not legal advice.

Editorial analysis

A component, not a grid framework

rsuite-table is described in one line as a React table component, and that word component is the whole scope of the project. There is no grid framework here, no data layer, and no opinion about where your rows come from.

The feature list is short and specific, and it is worth reading as the list of things that are usually hard in a table.

Virtualisation is first, which means the table only renders the rows that are visible, and the repository topics name it twice, as virtualization and windowing. Support for a fixed header and a fixed column comes next, which is the feature that separates a usable table from an unusable one in a scrollable page. There is support for custom adjustment of the column width, and for custom cell content, and for displaying a tree form, and for sorting, and for expandable child nodes, and for right to left.

The preview section shows four of them as images, because they are the features you cannot describe in a sentence. Fixed column, custom cell, tree table, and expandable.

The right to left support is worth a mention as a design signal. A table component that treats right to left as a checkbox is a table component written for a single market, and the presence of it in the core list, rather than in a separate build, means the layout primitives are direction aware.

The project is MIT licensed, written in TypeScript, documented at table.rsuitejs.com, and the last push was on 2026-10-20. The releases are 5.19.2 from 2025-07-04, 5.19.0 from 2024-08-23, and 5.18.3 from 2024-06-11.

The declarative model, in a few lines

The usage example is the fastest way to understand the API, because it shows that a table is a component with a data prop and a set of Column children, and that the cell content is a render function or a component.

tsx
import { Table, Column, HeaderCell, Cell } from 'rsuite-table';
import 'rsuite-table/lib/less/index.less'; // or 'rsuite-table/dist/css/rsuite-table.css'

const dataList = [
  { id: 1, name: 'a', email: '[email protected]', avartar: '...' },
  { id: 2, name: 'b', email: '[email protected]', avartar: '...' },
  { id: 3, name: 'c', email: '[email protected]', avartar: '...' }
];

Note the styling import on the second line. The package ships its own less file and a prebuilt stylesheet, and you import one of them yourself, which is the standard arrangement for a component library that does not want to inject styles into your page.

The table itself takes the data and its columns as children.

tsx
const App = () => (
  <Table data={dataList}>
    <Column width={100} sortable fixed resizable>
      <HeaderCell>ID</HeaderCell>
      <Cell dataKey="id" />
    </Column>
  </Table>
);

Three things are visible in that snippet. The Column takes its behaviour as flags, so sortable, fixed, and resizable are attributes of the column rather than configuration objects. The HeaderCell and Cell are children of the Column rather than props on it. And a Cell can carry a dataKey, which binds it to a field of the row object, or it can carry a render function, which is what the email column in the full example does to produce a mailto link.

There is also a custom cell component in the example, which wraps a Cell and renders an image from a field, and that pattern is the extension point for anything the built in cells do not cover.

The affix properties, and the table that pins itself to the page

The property table for the Table component is the API reference, and a few of its entries explain the fixed header and fixed column support better than the feature list does.

The affixHeader property takes a boolean or a number, and it affixes the table header to a specified position on the page. The affixHorizontalScrollbar property does the same for the horizontal scrollbar, affixing it to a specified position on the page.

That is the mechanism behind the fixed header and fixed column features, and the boolean or number signature is a useful detail. A boolean means the default behaviour, and a number means a specific offset, so a table whose header should sit below a fixed application bar can say so rather than being wrapped in another element to fake the spacing.

The height related properties are the ones with a documented conflict. The height property is the table height, with a default of 200, and the autoHeight property automatically expands the table height based on the number of data rows, without displaying a vertical scrollbar. The fillHeight property forces the table height to match its parent container height, and the documentation says explicitly that it cannot be used with autoHeight.

There are three ways to size a table in this API, and two of them conflict. That is the kind of thing that costs an hour if you do not read the table, because the symptom is a table that ignores its container.

The rest of the properties in the table cover the ordinary ground: bordered and cellBordered for the borders, hover which is on by default for the row effects, loading for a loading state, and the headerHeight property, which defaults to 40.

The children property deserves a note of its own, because it is unusual. It takes render props that receive parameterised Cell, HeaderCell, Column, and ColumnGroup components, and the stated purpose is to make TypeScript usage more convenient, which is a workaround for the fact that a component library cannot type the components it renders as children without help.

Tree rows, expansion, and controlled state

The expandable and tree features are related but distinct, and the property table shows how they are configured.

The isTree property displays the table as a tree structure, which is the hierarchical display with indentation and the disclosure triangles. The expandable child nodes feature is the other half, and it has a controlled and an uncontrolled form. The defaultExpandAllRows property expands all rows by default, and the defaultExpandedRowKeys property specifies the initially expanded rows by their keys, which is the uncontrolled version. The expandedRowKeys property does the same thing in the controlled form, where you hold the state yourself.

Having both forms is the right call, and the reason is that a table in a real application usually needs to know which rows are open, because the expansion state has to survive a re-render, has to be saved and restored, and often has to drive something else in the page.

The defaultSortType property takes either desc or asc, and it is the default sort type, so a table that has a sortable column starts in a defined order rather than in whatever order the data arrived.

And the data property is marked as required, which is a small but useful signal that this is a controlled component in the React sense. The table renders what you give it.

The preview images for the tree table and the expandable row are the clearest way to see the difference. The tree table is for data that is genuinely hierarchical, where the indentation carries meaning, and the expandable rows are for a flat list that happens to have children, such as a thread of replies.

Building, testing, and the shapes the package ships in

The package manifest is more interesting than a component library's usually is, because it says exactly how the artefact is built and what is checked before it is published.

The package is rsuite-table at version 5.19.2, with pnpm as the pinned package manager. It ships three builds: the main entry is the CommonJS build in the lib directory, the module field points at an ES module build in the es directory, and the typings point at the declaration file. The published file list includes the changelog, both build directories, the source, and a dist directory.

The build is two steps. The gulp task produces the bundles, and the types step runs the TypeScript compiler twice with declaration emit only, once into lib and once into es, so that both module formats carry their own type declarations rather than sharing one.

The interesting part is the test script, because it is the kind of rigour that separates a maintained component from a hobby one. The test command runs the lint, which is eslint plus a type check, then the karma suite in single run mode, then a full build, and then a separate type test against the test configuration. There is a separate postbuild step that runs a mocha test against the build output, which is the test that would catch a packaging mistake, such as a file that never made it into the published list.

The tooling is from a consistent era. Babel for the runtime, webpack for the bundles with a separate production configuration, gulp for the build, karma for the browser tests, prettier for formatting with a check variant, and conventional-changelog with the angular preset to generate the changelog.

There is a size script that runs the production build and then a size limit check, which is the mechanism that keeps a table component from quietly getting heavy, and a dev server on port 3100 bound to all interfaces for local work.

Editorial conclusion

rsuite-table fits a React team that needs a data grid with the awkward parts already handled, a sticky header, a fixed first column, a tree of rows, and a large dataset that needs windowing. It does not fit a team that needs a grid framework rather than a component, because the project is one component with a documented property list, and the styling is a less file you import yourself. Verify first that the column features you need are in the property list rather than assumed, that the height and auto height options are not set at the same time, since the documentation says they cannot be combined, and that the release you pin still matches the React version your application uses, because the package ships its own prebuilt stylesheet and its own type definitions.

Frequently asked questions

What features does rsuite-table support?

Virtualised rendering, a fixed header and a fixed column, custom column width adjustment, custom cell content, tree structure display, sorting, expandable child nodes, and right to left support. The preview section shows the fixed column, custom cell, tree table, and expandable variants.

How do I install rsuite-table?

With npm install rsuite-table, or the equivalent yarn add or pnpm add command. The package is published with a CommonJS build, an ES module build, and its own type declarations, and you import either the less source or the prebuilt stylesheet yourself.

How do I fix the header of an rsuite-table while it scrolls?

Use the affixHeader property on the Table, which takes a boolean or a number and affixes the header to a specified position on the page. There is a matching affixHorizontalScrollbar property for the horizontal scrollbar, and the fixed column feature is a flag on the Column.

Can I control which rows are expanded in an rsuite-table?

Yes. The expandedRowKeys property specifies the expanded rows by their keys and is the controlled form, while defaultExpandedRowKeys and defaultExpandAllRows are the uncontrolled options. The isTree property displays the table as a tree structure, which is separate from expansion.

How is the height of an rsuite-table controlled?

There are three options and they conflict. The height property sets the table height and defaults to 200, autoHeight expands the height to the number of rows without a vertical scrollbar, and fillHeight matches the parent container height, and the documentation says fillHeight cannot be used with autoHeight.

Official sources

  1. License: MIT
  2. Project website
  3. README
  4. Releases
  5. rsuite/rsuite-table on GitHub
Add this badge to your README

If you maintain this project, the badge below links readers to this analysis and shows its maintenance status from the daily GitHub snapshot. Paste the markdown into your README; add ?metric=license or ?metric=stars to the image URL for a different field.

Add this badge to your README

markdown
[![Hysen Labs](https://hysenlabs.com/badge/rsuite-rsuite-table.svg)](https://hysenlabs.com/projects/rsuite-rsuite-table)

Was this useful?