You're building a React application with a data table. Your users are viewing financial data, customer information, or inventory records—datasets with varying column widths. The "Description" column is too narrow, cutting off important text. The "ID" column is too wide, wasting precious screen space.
Column resizing solves this problem by letting users drag column borders to adjust widths according to their needs. It's a standard feature in Excel, Google Sheets, and professional data grids—and users expect it in modern web applications.
But implementing column resizing properly is surprisingly complex. You need to handle mouse events, calculate new widths, respect minimum and maximum constraints, work with auto-expanding columns, support touch devices, and maintain performance with large datasets.
This guide covers everything you need to know about column resizing in React tables: implementation approaches, library comparisons, and best practices for creating a smooth user experience.
Why Column Resizing Matters
Column resizing isn't just a "nice-to-have" feature—it's critical for usability in data-intensive applications:
User Control
Every user has different needs. Some need to see full email addresses, others prioritize status columns. Resizable columns give users control over their workspace.
Screen Real Estate
Different screens have different sizes. What works on a 27" monitor is cramped on a laptop. Resizable columns adapt to any screen size.
Data Visibility
Long text gets truncated with ellipsis. Users shouldn't need to hover or click to see full content—they can just widen the column.
Professional Feel
Users expect Excel-like functionality in professional applications. Resizable columns signal that your app is polished and production-ready.
Real-World Impact
In user testing, tables without column resizing consistently receive complaints:
- "I can't see the full customer name"
- "The ID column is taking up too much space"
- "Why can't I make this column wider?"
- "This feels like a prototype, not a real tool"
How Column Resizing Works
Column resizing involves several technical challenges that aren't immediately obvious:
1. The Resize Handle
The first challenge is creating a draggable handle between columns. This is typically a thin vertical area (4-8px wide) that:
- Changes the cursor to indicate it's draggable (usually a resize cursor)
- Captures mouse/touch events without interfering with header clicks
- Provides visual feedback during the drag operation
- Works on both desktop (mouse) and mobile (touch) devices
2. Width Calculation
As the user drags, you need to calculate the new column width based on mouse position. This involves:
1// Simplified resize calculation2const handleMouseMove = (e: MouseEvent) => {3 const deltaX = e.clientX - startX;4 const newWidth = Math.max(5 minWidth,6 Math.min(maxWidth, initialWidth + deltaX)7 );89 // Update column width10 setColumnWidth(columnId, newWidth);11};
3. Constraint Handling
Columns need constraints to prevent them from becoming unusably small or unnecessarily large:
Width Constraints
- minWidth: Prevents columns from becoming too narrow (typically 30-100px)
- maxWidth: Prevents columns from dominating the table (optional)
- Default width: Initial column width before user interaction
4. Auto-Expand Integration
The most complex scenario is combining column resizing with auto-expanding columns. When a user resizes one column, what happens to the others?
Two Common Approaches
When one column grows, adjacent columns shrink proportionally to maintain the total table width. This is ideal for tables that should always fill their container.
Resizing a column changes the total table width, potentially adding horizontal scrolling. This gives users maximum control but can lead to awkward layouts.
5. Performance Considerations
Resizing triggers re-renders of potentially thousands of cells. Poor implementations cause laggy, janky dragging. Good implementations use:
- Throttling/debouncing: Limit how often width updates occur during dragging
- CSS transforms: Use CSS for visual feedback before committing width changes
- Virtualization: Only render visible rows to minimize re-render cost
- Memoization: Prevent unnecessary re-renders of unchanged cells
React Table Libraries with Column Resizing
Let's compare how popular React table libraries implement column resizing:
| Library | Resizing Support | Implementation | Auto-Expand |
|---|---|---|---|
| Simple Table | Built-in | One prop | Yes |
| TanStack Table | Column Sizing API | Headless (build your own UI) | Manual |
| AG Grid | Built-in | resizable: true | Yes |
| Material React Table | Built-in | enableColumnResizing | Yes |
| Ant Design Table | Built-in | resizable prop | Limited |
| Tabulator | Built-in | resizableColumns | Yes |
| React Data Grid | Built-in | resizable column prop | Limited |
Key Takeaway
Most modern React table libraries support column resizing. The difference is in implementation complexity and integration with other features like auto-expanding columns. Simple Table and AG Grid offer the smoothest out-of-the-box experience, while TanStack Table requires more manual implementation.
Implementation: Simple Table
Simple Table makes column resizing incredibly easy. Just add one prop:
Basic Column Resizing
1import { SimpleTable, HeaderObject } from "simple-table-core";2import "simple-table-core/styles.css";34const headers: HeaderObject[] = [5 {6 accessor: "id",7 label: "ID",8 width: 60,9 type: "number"10 },11 {12 accessor: "name",13 label: "Name",14 width: "1fr",15 minWidth: 100,16 type: "string"17 },18 {19 accessor: "email",20 label: "Email",21 width: "1fr",22 minWidth: 150,23 type: "string"24 },25 {26 accessor: "role",27 label: "Role",28 width: 150,29 type: "string"30 },31];3233export default function ResizableTable({ data }) {34 return (35 <SimpleTable36 columnResizing={true} // That's it!37 defaultHeaders={headers}38 rows={data}3940 height="500px"41 />42 );43}
What You Get
- Drag handles appear between columns
- Smooth resize animation with visual feedback
- Minimum width constraints are automatically respected
- Works on both desktop (mouse) and mobile (touch)
- Integrates seamlessly with sorting, filtering, and other features
Column Resizing with Auto-Expand
The real power comes when you combine column resizing with autoExpandColumns. This ensures your table always fills the container width, even after users resize columns:
1<SimpleTable2 columnResizing={true}3 autoExpandColumns={true} // Columns always fill container4 defaultHeaders={headers}5 rows={data}67 height="500px"8/>910// What happens:11// 1. User drags a column border to resize12// 2. The target column changes width13// 3. Other columns automatically adjust proportionally14// 4. Table always fills 100% of container width15// 5. Minimum widths are respected (columns won't shrink below minWidth)
How Auto-Expand Works with Resizing
When you resize a column with autoExpandColumns enabled:
- Simple Table calculates the width change (delta)
- Adjacent columns compensate by shrinking/growing proportionally
- The algorithm respects each column's minimum width constraint
- The total table width remains constant (100% of container)
Read our deep dive on auto-expand columns to understand the sophisticated algorithm behind this feature.
Setting Width Constraints
Control how much users can resize columns using minWidth and maxWidth:
1const headers: HeaderObject[] = [2 {3 accessor: "id",4 label: "ID",5 width: 60,6 minWidth: 40, // Can't shrink below 40px7 maxWidth: 100, // Can't grow beyond 100px8 type: "number"9 },10 {11 accessor: "description",12 label: "Description",13 width: "1fr",14 minWidth: 200, // Ensure readability15 type: "string"16 },17 {18 accessor: "status",19 label: "Status",20 width: 120,21 minWidth: 80, // Prevent cramping22 maxWidth: 200, // Don't let it dominate23 type: "string"24 },25];
Best Practices
- Always set minWidth: Prevents columns from becoming unusably narrow
- Use maxWidth sparingly: Only when a column shouldn't dominate the table
- Consider content: Text columns need more width than icons or numbers
- Test on mobile: Minimum widths should work on small screens
Responsive Column Resizing
On mobile devices, you might want to disable auto-expand to allow horizontal scrolling:
1import { useState, useEffect } from "react";23export default function ResponsiveTable({ data }) {4 const [isMobile, setIsMobile] = useState(false);56 useEffect(() => {7 const checkMobile = () => setIsMobile(window.innerWidth < 768);8 checkMobile();9 window.addEventListener("resize", checkMobile);10 return () => window.removeEventListener("resize", checkMobile);11 }, []);1213 return (14 <SimpleTable15 columnResizing={true}16 autoExpandColumns={!isMobile} // Disable on mobile17 defaultHeaders={headers}18 rows={data}1920 height="500px"21 />22 );23}
Implementation: TanStack Table
TanStack Table provides a Column Sizing API, but you need to build the UI yourself. Here's what that looks like:
1import { useReactTable, getCoreRowModel, ColumnDef } from "@tanstack/react-table";2import { useState } from "react";34export default function TanStackResizableTable({ data }) {5 const [columnSizing, setColumnSizing] = useState({});67 const columns: ColumnDef<any>[] = [8 {9 accessorKey: "id",10 header: "ID",11 size: 60,12 minSize: 40,13 maxSize: 100,14 },15 {16 accessorKey: "name",17 header: "Name",18 size: 200,19 minSize: 100,20 },21 // ... more columns22 ];2324 const table = useReactTable({25 data,26 columns,27 getCoreRowModel: getCoreRowModel(),28 columnResizeMode: "onChange",29 state: {30 columnSizing,31 },32 onColumnSizingChange: setColumnSizing,33 });3435 return (36 <table>37 <thead>38 {table.getHeaderGroups().map((headerGroup) => (39 <tr key={headerGroup.id}>40 {headerGroup.headers.map((header) => (41 <th42 key={header.id}43 style={{ width: header.getSize() }}44 >45 {header.column.columnDef.header}4647 {/* YOU MUST BUILD THIS RESIZE HANDLE */}48 <div49 onMouseDown={header.getResizeHandler()}50 onTouchStart={header.getResizeHandler()}51 className="resizer"52 style={{53 position: "absolute",54 right: 0,55 top: 0,56 height: "100%",57 width: "5px",58 cursor: "col-resize",59 userSelect: "none",60 touchAction: "none",61 }}62 />63 </th>64 ))}65 </tr>66 ))}67 </thead>68 {/* ... tbody implementation ... */}69 </table>70 );71}
TanStack Table Trade-offs
Pros:
- Complete control over UI and behavior
- Powerful Column Sizing API with state management
- Flexible integration with your design system
Cons:
- Must build resize handles, styling, and interactions yourself
- No built-in auto-expand logic (you implement it)
- More code to write and maintain
- Steeper learning curve
For a detailed comparison of TanStack Table's headless approach vs Simple Table's batteries-included approach, read our TanStack Table vs Simple Table comparison.
Implementation: AG Grid
AG Grid offers powerful column resizing, but it comes with a hefty price tag for advanced features:
1import { AgGridReact } from "ag-grid-react";2import "ag-grid-community/styles/ag-grid.css";3import "ag-grid-community/styles/ag-theme-alpine.css";45export default function AGGridResizableTable({ data }) {6 const columnDefs = [7 {8 field: "id",9 headerName: "ID",10 width: 60,11 minWidth: 40,12 maxWidth: 100,13 resizable: true // Enable per-column14 },15 {16 field: "name",17 headerName: "Name",18 width: 200,19 minWidth: 100,20 resizable: true,21 flex: 1 // Auto-expand behavior22 },23 // ... more columns24 ];2526 return (27 <div className="ag-theme-alpine" style={{ height: 500 }}>28 <AgGridReact29 columnDefs={columnDefs}30 rowData={data}31 defaultColDef={{32 resizable: true // Or enable globally33 }}34 />35 </div>36 );37}
AG Grid Pricing Warning
While column resizing is free in AG Grid Community, many related features require an Enterprise license:
- $999/developer/year for AG Grid Enterprise
- Advanced column features (grouping, pivoting) are enterprise-only
- For a 5-developer team: $4,995/year
Read our complete AG Grid pricing breakdown to understand the full cost.
Common Pitfalls and Solutions
Pitfall #1: Janky Resize Performance
Problem: Dragging feels laggy because every mouse movement triggers a full table re-render.
Solution: Use throttling/debouncing or CSS transforms for visual feedback before committing width changes. Good libraries handle this automatically.
Pitfall #2: Columns Become Too Narrow
Problem: Users can shrink columns to 10px, making content unreadable.
Solution: Always set reasonable minWidth values (typically 50-100px for text columns, 30-50px for icons/numbers).
Pitfall #3: Mobile Touch Not Working
Problem: Column resizing works on desktop but not on mobile devices.
Solution: Ensure your resize handles support both onMouseDown and onTouchStart events. Libraries like Simple Table handle this automatically.
Pitfall #4: Auto-Expand Conflicts
Problem: When using auto-expand, resizing one column causes unexpected behavior in others.
Solution: Use a library with built-in auto-expand support (Simple Table, AG Grid) or implement sophisticated compensation logic yourself.
Pitfall #5: Resize Handle Too Small
Problem: Users struggle to grab the resize handle because it's only 1-2px wide.
Solution: Make the interactive area 6-10px wide, even if the visual indicator is thinner. This improves usability without cluttering the UI.
Best Practices for Column Resizing
Visual Feedback
Show clear visual feedback during resizing: change cursor, highlight the column border, or show a ghost column. Users should know exactly what's happening.
Sensible Defaults
Set default column widths that work for most users. They can adjust if needed, but shouldn't have to resize every column just to see their data.
Persist User Preferences
Save column widths to localStorage or user preferences. Users shouldn't have to resize columns every time they visit your app.
Double-Click to Auto-Size
Let users double-click a resize handle to auto-fit the column to its content. This is an Excel convention that power users expect.
Mobile Considerations
On small screens, consider disabling auto-expand and allowing horizontal scrolling. Cramped columns are worse than scrolling on mobile.
Accessibility
Ensure resize handles are keyboard accessible. Users should be able to resize columns using arrow keys, not just mouse/touch.
Which Library Should You Choose?
Here's a quick decision guide based on your needs:
Choose Simple Table if:
- You want column resizing to work out-of-the-box with one prop
- You need auto-expand columns that integrate seamlessly with resizing
- You want zero dependencies and a tiny bundle size
- You need a complete solution without building UI components
- You want to ship fast without wrestling with configuration
Choose TanStack Table if:
- You need complete control over the resize UI and behavior
- You're building a custom design system and want headless logic
- You enjoy building UI components from scratch
- You have time to implement auto-expand logic yourself
- You need framework-agnostic table logic (React, Vue, Solid, etc.)
Choose AG Grid if:
- You need enterprise features like pivoting and advanced grouping
- Your company has budget for $999/developer/year licenses
- You're building a complex financial or analytics dashboard
- You need Excel-like functionality with enterprise support
Note: If you don't need enterprise features, consider free AG Grid alternatives that offer similar functionality without the cost.
Choose Material React Table if:
- You're already using Material-UI in your app
- You want TanStack Table power with pre-built Material Design components
- You're okay with a larger bundle size for the convenience
- You need your table to match Material-UI's design language
Conclusion: Column Resizing Done Right
Column resizing is a fundamental feature for data-heavy applications. Users expect it, and implementing it properly makes the difference between a frustrating experience and a professional, polished application.
While the feature seems simple on the surface, proper implementation involves handling mouse/touch events, managing width constraints, integrating with auto-expand logic, and maintaining performance. The good news is that modern React table libraries handle most of this complexity for you.
Simple Table offers the easiest path: enable column resizing with one prop, and it works seamlessly with auto-expanding columns, sorting, filtering, and all other features. No configuration, no UI building, no headaches.
Ready to add column resizing to your React app? Check out Simple Table's column resizing documentation and see how easy it can be.
Ready to add column resizing to your React table?
Simple Table makes column resizing effortless with just one prop. Get drag-to-resize, auto-expand integration, and smooth performance out of the box. No configuration, no UI building, no headaches.