Column resizing is one of those features users miss only when it's gone. The moment a content cell truncates with an ellipsis, somebody reaches for the column edge and tries to drag.
This tutorial walks through column resizing patterns for the vanilla JavaScript / TypeScript data grid landscape—Tabulator, Grid.js, Handsontable, jSpreadsheet—and shows the simple-table-core setup for framework-agnostic apps.
If you want a strict-TypeScript, ESM-first, MIT-licensed core grid that you can mount in any framework or no framework, simple-table-core is ~70 kB gzipped with built-in resize, pinning, and virtualization.
Why it matters
Density vs scannability
Users adjust columns to suit their screen and the row data they're scanning—you don't have to guess for them.
Excel-like ergonomics
Power users expect drag-handle resizing on column edges; without it, the grid feels broken.
Pinned-column compatibility
Resizing a pinned column has to recalc the sticky offset; a quality grid handles this for you.
Framework-agnostic mounting
Vanilla grids ship in any host: web components, micro-frontends, server-rendered pages.
Vanilla JS / TypeScript library comparison
| Library | Support | Notes |
|---|---|---|
| simple-table-core | Built-in | columnResizing: true in options; strict TypeScript types out of the box. |
| Tabulator | Built-in | resizableColumns: true; full-featured but heavier (~250 kB gzipped). |
| Grid.js | Manual | No native resize—use plugins or DIY CSS handles. |
| Handsontable | Built-in (commercial) | Resize built-in but commercial license required for non-personal use. |
| jSpreadsheet | Built-in | Spreadsheet-style resize; less suited for typed data grids. |
Implementation: simple-table-core
Construct a SimpleTableVanilla instance, point it at a host element, and pass columnResizing in options. Strict TypeScript types make headers and rows safe at compile time.
table.dispose() if your host element is removed (e.g. SPA route changes) to avoid event-listener leaks.Common pitfalls
Memory leaks on SPA navigation
Problem: Mounting in a SPA without disposing leaks listeners.
Solution: Always call table.dispose() on cleanup; in micro-frontends, hook this to your unmount lifecycle.
Resize handles too narrow on touch
Problem: Mobile users can't grab the 4px column edge.
Solution: Use a library that auto-widens handles for touch (simple-table-core does this).
Bundle size
Problem: Tabulator and Handsontable add 200-400 kB to your bundle.
Solution: If you don't need every feature, simple-table-core is ~70 kB gzipped with the same core capabilities.
TypeScript types missing
Problem: Some grids ship loose any-typed APIs.
Solution: simple-table-core is strict-TypeScript-first; HeaderObject, Row, and event payloads are all typed.
Frequently asked questions
- Can I use this in a web component?
- Yes. Mount inside the shadow root: pass shadowRoot.querySelector('#host') as the element. simple-table-core uses CSS variables for theming, so it inherits styles you set inside the shadow tree.
- Does it work without a build step?
- Yes. simple-table-core ships ESM via a CDN like esm.sh; you can <script type="module"> in plain HTML.
- How do I handle SSR?
- Render an empty host on the server, then mount the grid client-side after hydration. simple-table-core only touches the DOM in the constructor.
Wrap-up
Column resizing in vanilla JS / TS is a single option on simple-table-core. Tabulator and Handsontable ship it but at much larger bundle sizes; Grid.js requires DIY.
If you need a framework-agnostic, strict-TypeScript core grid with resize, pinning, and virtualization, simple-table-core is the focused MIT pick—~70 kB gzipped.