# react-base-table ignores functions when comparing columns, and that has consequences

> BaseTable is a virtualized React table for large datasets, built on react-window, and it is designed as the base layer of a table component you write yourself rather than a finished grid. The interesting engineering is in how it compares column definitions to decide whether to re-render, and in the stale-value bug that decision creates.

**Autodesk/react-base-table** — A react table component to display large datasets with high performance and flexibility

- Repository: https://github.com/Autodesk/react-base-table
- Website: https://autodesk.github.io/react-base-table/
- Stars: 1,541 · Forks: 171
- Language: TypeScript
- License: MIT
- Published: 2026-10-16 · Updated: 2026-10-16 · Language: en
- Canonical page: https://hysenlabs.com/projects/autodesk-react-base-table

## Virtualization comes from react-window, not from this package

BaseTable is described as a React table component to display large datasets with high performance and flexibility, and the performance claim has a specific source. The runtime dependencies are `react-window` for windowing and `react-virtualized-auto-sizer` for measuring the container, with `memoize-one` and `classnames` alongside them.

So the virtualized rendering, which is the whole reason to reach for a table like this rather than a plain map of rows, is delegated to `react-window`. That has two consequences. First, only the visible window plus a small overscan exists in the DOM, so row count stops being the limiting factor. Second, every row height, overscan and scrolling behaviour question is answered by that library's behaviour rather than by this one.

The component API is small. `BaseTable` takes data and dimensions, and columns are declared with a `Column` child rather than a columns array, which means a column definition is a React element in your tree:

```bash
npm install react-base-table --save
```

```tsx
import BaseTable, { Column } from 'react-base-table'
import 'react-base-table/dist/styles.css'

<BaseTable data={data} width={600} height={400}>
  <Column key="col0" dataKey="col0" width={100} />
  <Column key="col1" dataKey="col1" width={100} />
</BaseTable>
```

That stylesheet import is not optional. The README marks it as a requirement for correct rendering, and the failure mode if you omit it is a table with no layout rather than an error message.

## Two silent requirements: column key and row key

Two identifiers have to be correct before anything else works, and neither throws when it is wrong.

The first is `key` on a column. The README states it is required for column definition and that a column without it will be ignored. Ignored, not reported. A column that silently does not appear is one of the more expensive mistakes to debug from the outside, because nothing in React's output tells you the key was the problem.

The second is the row key. Every item in `data` must be unique by a key, the default is `id`, and `rowKey` customizes it. This is the prop people forget when their data has no `id` field, and the result is selection, expansion and hover behaving incorrectly on rows that share a key.

Sizing follows the same pattern of required but unforgiving. `width` is required for a column definition, and `width` and `height`, or `maxHeight`, are required for the table to display properly. There is a documented escape from the fixed width problem: in flex mode, set `fixed={false}` and then use `width={0}` with `flexGrow={1}` so the column takes the space that is left.

For a table that should fill a responsive container, the `AutoResizer` component is the documented route, and the examples wrap `BaseTable` in a small component that supplies fixed dimensions to keep the examples short.

## The closure problem is the real cost of this design

There is a section of the README devoted to a rendering subtlety, and it is the part to read before committing to this component.

Custom renderers are usually written as inline functions. An inline function creates a new instance on every re-render, which would make the `shouldUpdateComponent` check always true and defeat the purpose of comparing. To avoid that, BaseTable ignores functions when comparing column definitions by default.

That optimisation has a price. If a custom renderer closes over external data instead of reading it from reference state, you get the stale initial value even though the data has changed, because the comparison said nothing changed and the row was never re-rendered.

The README gives a workaround and then admits its downside. Inject the external data into the column definition itself, so the cell renderer destructures it from the column, for example `cellRenderer={({ column: { foo, bar }}) => { ... } }`. The column definition updates when the external data changes. The cost is that the column definition gets bloated and becomes bug prone.

The cleaner answer arrived later and is the more interesting one. With the introduction of React hooks the library moved to primitive state rather than `this.state`, which makes closures easy to hit, but hooks also make them easy to fix: memoize with `useCallback` or `useMemo`. The implicit optimisation can be turned off by setting `ignoreFunctionInColumnCompare` to `false`, an option introduced in v1.11.0.

So the honest summary is that the default is a heuristic that trades correctness for speed, and turning it off moves the optimisation into your own code where you can see it.

## classPrefix and components are the extension points

The advanced section states the intent plainly: BaseTable is designed as the base component to build your own complex table component. It is not a grid with features, it is the layer under one.

Three extension points carry that design.

`classPrefix` renames the CSS classes the component emits, so `AdvanceTable` instead of the default. `components` swaps out individual pieces, and the example replaces `TableCell`, `TableHeaderCell`, `ExpandIcon` and `SortIndicator` with your own React components. Everything else, including all of the virtualizing and measurement behaviour, stays.

Styling is offered three ways. The simplest is overriding default SCSS variables and re-importing the partial, with a set of variables that include `$table-prefix`, `$table-font-size`, `$table-padding-left`, `$table-padding-right`, `$column-padding`, and the two shadow switches `$show-frozen-rows-shadow` and `$show-frozen-columns-shadow`:

```scss
$table-prefix: AdvanceTable;
$show-frozen-columns-shadow: true;

@import 'react-base-table/dist/esm/_BaseTable.scss';
```

The imported partial uses the prefix variable, so your classes and the component's classes agree. You can also write styles from scratch or use a CSS-in-JS solution, both of which the README names as supported.

Beyond styling, the component exposes a large set of `xxxRenderer` and `xxxProps` pairs, and the documentation points to an API reference rather than enumerating them. That is a fair trade for a component intended to be wrapped, and it does mean you should budget an afternoon reading the API page before estimating the work.

The proof of the approach is the advanced table the project builds on top: row selection, row grouping, data aggregation, column settings, column reordering and grouping, tooltips and inline editing. None of that ships in this package.

## Package version 2.1.0 against a release tag of v1.11.1

There is a discrepancy worth knowing about before you pick a version.

The version in package.json is 2.1.0, while the most recent GitHub release is v1.11.1, published on 2020-08-17. v1.11.0 is from the same day, while v1.10.5 is from 2020-07-10. 

The last push was on 2026-08-05, so the codebase is being worked on. What the release history does not tell you is whether the 2.x line is finished or in progress, and the README does not describe a migration from 1.x to 2.x. The `CHANGELOG.md` at the repository root is where that would be, so read it before upgrading an existing table.

The build setup explains the type story. The package is written in TypeScript and ships declarations, with `build:types` running `tsc --emitDeclarationOnly` and public types exported from the main entry:

```ts
import type { ColumnShape, RowData, SortOrderValue } from 'react-base-table'
```

The output is split three ways, CommonJS at `dist/cjs/index.js`, ES modules at `dist/esm/index.js` and types at `dist/types/index.d.ts`, with the stylesheet compiled separately by `sass` into `dist/styles.css`. Tests run on jest, and the repository uses husky with lint-staged so formatting and linting run on staged source.

Browser support is stated as modern browsers only, Chrome, Firefox, Safari and Edge, and IE is no longer supported. Development needs Node.js 18 or newer with Node 24 recommended, and the project uses Yarn.

## Where react-base-table is the wrong choice

Three cases.

If you want a grid with row selection, grouping, aggregation, column reordering and inline editing already working, this is the wrong package. All of that exists in the project's own advanced table, which is built on top of BaseTable and is not shipped in it. Using BaseTable directly means writing those features, which is the point of it and also its cost.

If your dataset is small enough to fit on screen without virtualization, you are paying for windowing complexity, the column comparison heuristic and its stale-value trap in exchange for nothing.

If your team has no appetite for the extension points, the component prefixing and the SCSS variable overriding are extra work compared with a grid whose defaults you accept.

There is one more risk to weigh, and it is about your own code rather than the library. The default `ignoreFunctionInColumnCompare` behaviour will hide real updates inside a custom renderer, and the fix depends on you restructuring a cell renderer to take data from the column definition or to memoise it with hooks. On a table with twenty custom renderers that is a migration, not a tweak.

The licence is MIT and the package ships only `dist/`, the licence and the readme, so nothing unexpected arrives in your dependency tree.

## Conclusion

Adopt BaseTable when you are building a grid with row grouping, aggregation or inline editing and want to own the rendering layer yourself; skip it when you want a ready-made grid with built-in selection and sorting, since the project's own advanced example lives outside this package. Verify first that every `data` item has a unique key, defaulting to `id` and configurable with `rowKey`, and import the stylesheet for correct rendering.

## FAQ

### How do I install react-base-table and why do I need the CSS import?

Install it with `npm install react-base-table --save` or `yarn add react-base-table`. The stylesheet import is required for correct rendering, so add `import 'react-base-table/dist/styles.css'` alongside the component import.

### Why does my column not render?

A `key` is required on every column definition and a column without one is ignored rather than reported. Also check that each item in `data` is unique by a key, since the default key is `id` and it can be changed with `rowKey`.

### My custom cell renderer shows stale data, how do I fix it?

BaseTable ignores functions when comparing column definitions, so a renderer closing over external data can keep its initial value. Pass the data into the column definition and read it from the column, memoise the renderer with `useCallback` or `useMemo`, or set `ignoreFunctionInColumnCompare` to `false`, an option added in v1.11.0.

## Sources

- [Autodesk/react-base-table on GitHub](https://github.com/Autodesk/react-base-table)
- [License: MIT](https://github.com/Autodesk/react-base-table/blob/master/LICENSE)
- [Project website](https://autodesk.github.io/react-base-table/)
- [README](https://github.com/Autodesk/react-base-table/blob/master/README.md)
- [Releases](https://github.com/Autodesk/react-base-table/releases)

---

Hysen Labs editorial analysis, written from the project's own repository and release notes. Cite the canonical page: https://hysenlabs.com/projects/autodesk-react-base-table
