Implementing a Fixed Column Table in React

Recently, I faced a challenge that many React developers encounter: building a table with a fixed column while allowing the rest of the table to scroll. Up until now, I had been working extensively with React Data Table Component (RDT), which I genuinely love.
Why I Loved React Data Table Component
React Data Table Component has been a perfect fit for most of my projects. Some of the advantages I enjoy:
- Simple setup: Minimal configuration to get a fully functional table.
- Built-in features: Sorting, filtering, pagination, and customizable styling come out of the box.
- Lightweight and flexible: Works well without bloating the app.
- Developer-friendly API: Intuitive props and components make it easy to extend.
Here's an example of what I've been able to create with RDT:
For most use cases, it’s amazing. But when I needed a fixed column, I quickly realized that React Data Table Component wasn’t going to cut it. Attempts to tweak it were cumbersome and even broke parts of the app. I needed another solution.
Evaluating Options: AG Grid vs TanStack Table
At this point, I had two realistic alternatives: AG Grid and TanStack Table. Here’s a breakdown of both.
1. AG Grid
AG Grid is a powerful data grid for React (and other frameworks), widely used in enterprise apps.
Pros:
- Built-in support for fixed/frozen columns.
- Highly configurable with advanced features: row grouping, pivot tables, Excel-like filtering.
- Excellent documentation and community support.
Cons:
- Large bundle size: Including the full AG Grid library can be heavy if you only need a few features.
- Complexity: Many advanced features are unnecessary for simple use cases.
Installation and Example
1npm install ag-grid-react ag-grid-community1import { AgGridReact } from 'ag-grid-react';
2import 'ag-grid-community/styles/ag-grid.css';
3import 'ag-grid-community/styles/ag-theme-alpine.css';
4
5const columnDefs = [
6 { headerName: "ID", field: "id", pinned: "left" },
7 { headerName: "Name", field: "name" },
8 { headerName: "Age", field: "age" },
9];
10
11const rowData = [
12 { id: 1, name: "Alice", age: 25 },
13 { id: 2, name: "Bob", age: 30 },
14];
15
16<AgGridReact
17 className="ag-theme-alpine"
18 columnDefs={columnDefs}
19 rowData={rowData}
20/>
21
AG Grid would have given me the easiest path to a perfect-looking table. But the size and overhead felt excessive for my needs.
TanStack Table (React Table)
TanStack Table is a headless utility for building fully customizable tables. Unlike AG Grid, it doesn’t provide a UI out of the box, but it gives you full control over rendering and behavior.
Pros:
- Lightweight and composable: Build only what you need.
- Advanced features like sorting, filtering, and pagination are supported.
- Excellent TypeScript support.
Cons:
- Slightly more setup required compared to plug-and-play libraries.
- Styling and scroll behaviors need to be implemented manually.
Installation & Example:
1npm install @tanstack/react-table1import { useReactTable, createColumnHelper } from '@tanstack/react-table';
2
3const columnHelper = createColumnHelper();
4const columns = [
5 columnHelper.accessor('id', { header: 'ID' }),
6 columnHelper.accessor('name', { header: 'Name' }),
7 columnHelper.accessor('age', { header: 'Age' }),
8];
9
10const data = [
11 { id: 1, name: 'Alice', age: 25 },
12 { id: 2, name: 'Bob', age: 30 },
13];
14
15function MyTable() {
16 const table = useReactTable({ data, columns });
17
18 return (
19 <table>
20 <thead>
21 {table.getHeaderGroups().map(headerGroup => (
22 <tr key={headerGroup.id}>
23 {headerGroup.headers.map(header => (
24 <th key={header.id}>{header.renderHeader()}</th>
25 ))}
26 </tr>
27 ))}
28 </thead>
29 <tbody>
30 {table.getRowModel().rows.map(row => (
31 <tr key={row.id}>
32 {row.getVisibleCells().map(cell => (
33 <td key={cell.id}>{cell.renderCell()}</td>
34 ))}
35 </tr>
36 ))}
37 </tbody>
38 </table>
39 );
40}
41
Using TanStack Table, I could implement the fixed column with a small CSS wrapper, without importing a massive library. The setup required a bit more work, but the tradeoffs were worth it.
Pro Tip: You can combine TanStack Table with position: sticky and overflow-x: auto to achieve fixed/frozen column behavior.
Here's what the final output looked like with the sticky column
The Lesson: Tradeoffs Matter
This experience reinforced an important principle:
The “best” solution in terms of output isn’t always the best choice overall.
AG Grid offered immediate perfection, but the cost in bundle size and complexity was too high for a simple feature. TanStack Table required a little more work upfront, but it aligned with my project’s goals: lightweight, flexible, and performant.
When making decisions, always consider:
- Performance & bundle size
- Complexity vs. maintainability
- Scalability for future features
Sometimes the solution that isn’t “perfect” in the UI sense is actually the smarter, more sustainable choice.
Conclusion
Choosing the right tool isn’t about which library has the most features. It’s about finding the one that fits your project, aligns with performance needs, and is maintainable for the long run.
For me, TanStack Table hit the sweet spot: lightweight, flexible, and customizable enough to implement a fixed column. AG Grid could have done the job more easily, but the tradeoffs didn’t justify it.
In software development, tradeoffs are everywhere. Understanding them and making informed choices is what separates a good developer from a thoughtful one.