Documentation
Column Resizing
Let users adjust column widths by dragging header dividers — or double-click to auto-fit.
Enable resizing
Set columnResizing to let users drag header dividers. Double-click a handle to auto-fit that column to its content.
React TSX
Copy
<SimpleTable columns={columns} rows={rows} height="400px" columnResizing={true} />
Vue SFC
Copy
<SimpleTable:columns="columns":rows="rows"height="400px":column-resizing="true"/>
Angular
Copy
<simple-table[columns]="columns"[rows]="rows"height="400px"[columnResizing]="true"></simple-table>
Svelte
Copy
<SimpleTable {columns} {rows} height="400px" columnResizing={true} />
Solid TSX
Copy
<SimpleTable columns={columns} rows={rows} height="400px" columnResizing={true} />
TypeScript
Copy
new SimpleTableVanilla(container, {columns,rows,height: "400px",columnResizing: true,});
Persist column widths
Use onColumnWidthChange to save widths whenever the user resizes or auto-fits a column.
React TSX
Copy
const handleColumnWidthChange = (headers) => {const widths = Object.fromEntries(headers.map((h) => [h.accessor, h.width]));localStorage.setItem("columnWidths", JSON.stringify(widths));};<SimpleTablecolumnResizingcolumns={columns}rows={rows}onColumnWidthChange={handleColumnWidthChange}/>
Vue SFC
Copy
<script setup>const handleColumnWidthChange = (headers) => {const widths = Object.fromEntries(headers.map((h) => [h.accessor, h.width]));localStorage.setItem("columnWidths", JSON.stringify(widths));};</script><template><SimpleTable:column-resizing="true":columns="columns":rows="rows"@column-width-change="handleColumnWidthChange"/></template>
Angular
Copy
const handleColumnWidthChange = (headers) => {const widths = Object.fromEntries(headers.map((h) => [h.accessor, h.width]));localStorage.setItem("columnWidths", JSON.stringify(widths));};<simple-table[columnResizing]="true"[columns]="columns"[rows]="rows"[onColumnWidthChange]="handleColumnWidthChange"></simple-table>
Svelte
Copy
<script>const handleColumnWidthChange = (headers) => {const widths = Object.fromEntries(headers.map((h) => [h.accessor, h.width]));localStorage.setItem("columnWidths", JSON.stringify(widths));};</script><SimpleTablecolumnResizing={true}{columns}{rows}onColumnWidthChange={handleColumnWidthChange}/>
Solid TSX
Copy
const handleColumnWidthChange = (headers) => {const widths = Object.fromEntries(headers.map((h) => [h.accessor, h.width]));localStorage.setItem("columnWidths", JSON.stringify(widths));};<SimpleTablecolumnResizingcolumns={columns}rows={rows}onColumnWidthChange={handleColumnWidthChange}/>
TypeScript
Copy
const handleColumnWidthChange = (headers) => {const widths = Object.fromEntries(headers.map((h) => [h.accessor, h.width]));localStorage.setItem("columnWidths", JSON.stringify(widths));};new SimpleTableVanilla(container, {columns,rows,columnResizing: true,onColumnWidthChange: handleColumnWidthChange,});
Example
Drag dividers or double-click to auto-fit. This demo saves widths to localStorage.
React TSX
Copy
1import { useState, useEffect } from "react";2import { SimpleTable } from "@simple-table/react";3import type { Theme, ReactColumnDef } from "@simple-table/react";4import {5 columnResizingHeaders,6 columnResizingData,7 COLUMN_RESIZING_STORAGE_KEY,8} from "./column-resizing.demo-data";9import "@simple-table/react/styles.css";1011const ColumnResizingDemo = ({12 height = "400px",13 theme,14}: {15 height?: string | number;16 theme?: Theme;17}) => {18 const [headers, setHeaders] = useState(() => columnResizingHeaders);19 const [saveMessage, setSaveMessage] = useState("");2021 useEffect(() => {22 try {23 const saved = localStorage.getItem(COLUMN_RESIZING_STORAGE_KEY);24 if (saved) {25 const widthMap = JSON.parse(saved);26 setHeaders(27 columnResizingHeaders.map((h) => ({28 ...h,29 width: (widthMap as Record<string, number | string | undefined>)[h.accessor] ?? h.width,30 })),31 );32 }33 } catch {34 // ignore35 }36 }, []);3738 const handleColumnWidthChange = (updatedHeaders: ReactColumnDef[]) => {39 try {40 const widthMap = updatedHeaders.reduce(41 (acc, h) => {42 acc[h.accessor] = h.width;43 return acc;44 },45 {} as Record<string, number | string>,46 );47 localStorage.setItem(COLUMN_RESIZING_STORAGE_KEY, JSON.stringify(widthMap));48 setHeaders(updatedHeaders);49 setSaveMessage("Column widths saved!");50 setTimeout(() => setSaveMessage(""), 2000);51 } catch {52 setSaveMessage("Failed to save widths");53 setTimeout(() => setSaveMessage(""), 2000);54 }55 };5657 return (58 <div style={{ position: "relative", height: "100%" }}>59 {saveMessage && (60 <div61 style={{62 position: "absolute",63 top: 8,64 right: 8,65 background: "#10b981",66 color: "white",67 padding: "8px 16px",68 borderRadius: 6,69 fontSize: 14,70 fontWeight: 500,71 zIndex: 1000,72 boxShadow: "0 2px 8px rgba(0,0,0,0.15)",73 }}74 >75 {saveMessage}76 </div>77 )}78 <SimpleTable79 columnResizing80 columns={headers}81 rows={columnResizingData}82 height={height}83 theme={theme}84 onColumnWidthChange={handleColumnWidthChange}85 />86 </div>87 );88};8990export default ColumnResizingDemo;
Vue SFC
Copy
1<template>2 <div style="position: relative; height: 100%">3 <div4 v-if="saveMessage"5 style="position: absolute; top: 8px; right: 8px; background: #10b981; color: white; padding: 8px 16px; border-radius: 6px; font-size: 14px; font-weight: 500; z-index: 1000; box-shadow: 0 2px 8px rgba(0,0,0,0.15);"6 >7 {{ saveMessage }}8 </div>9 <SimpleTable10 :column-resizing="true"11 :columns="headers"12 :rows="columnResizingData"13 :height="height"14 :theme="theme"15 @column-width-change="handleColumnWidthChange"16 />17 </div>18</template>1920<script setup lang="ts">21import { ref, onMounted } from "vue";22import { SimpleTable } from "@simple-table/vue";23import type { Theme, VueColumnDef } from "@simple-table/vue";24import { columnResizingHeaders, columnResizingData, COLUMN_RESIZING_STORAGE_KEY } from "./column-resizing.demo-data";25import "@simple-table/vue/styles.css";2627withDefaults(defineProps<{ height?: string | number; theme?: Theme }>(), {28 height: "400px",29});3031const headers = ref<VueColumnDef[]>([...columnResizingHeaders]);32const saveMessage = ref("");3334onMounted(() => {35 try {36 const saved = localStorage.getItem(COLUMN_RESIZING_STORAGE_KEY);37 if (saved) {38 const widthMap = JSON.parse(saved);39 headers.value = columnResizingHeaders.map((h) => ({40 ...h,41 width: widthMap[h.accessor] ?? h.width,42 }));43 }44 } catch { /* ignore */ }45});4647function handleColumnWidthChange(updatedHeaders: VueColumnDef[]) {48 try {49 const widthMap = updatedHeaders.reduce(50 (acc: Record<string, unknown>, h) => { acc[h.accessor] = h.width; return acc; },51 {},52 );53 localStorage.setItem(COLUMN_RESIZING_STORAGE_KEY, JSON.stringify(widthMap));54 headers.value = updatedHeaders;55 saveMessage.value = "Column widths saved!";56 setTimeout(() => { saveMessage.value = ""; }, 2000);57 } catch {58 saveMessage.value = "Failed to save widths";59 setTimeout(() => { saveMessage.value = ""; }, 2000);60 }61}62</script>
Angularcolumn-resizing-demo.component.ts
Copy
1import { NgIf } from "@angular/common";2import { Component, Input, OnInit } from "@angular/core";3import { SimpleTableComponent } from "@simple-table/angular";4import type { AngularColumnDef, Row, Theme } from "@simple-table/angular";5import { columnResizingHeaders, columnResizingData, COLUMN_RESIZING_STORAGE_KEY } from "./column-resizing.demo-data";6import "@simple-table/angular/styles.css";78@Component({9 selector: "column-resizing-demo",10 standalone: true,11 imports: [SimpleTableComponent, NgIf],12 template: `13 <div style="position: relative; height: 100%">14 <div15 *ngIf="saveMessage"16 style="position: absolute; top: 8px; right: 8px; background: #10b981; color: white; padding: 8px 16px; border-radius: 6px; font-size: 14px; font-weight: 500; z-index: 1000; box-shadow: 0 2px 8px rgba(0,0,0,0.15);"17 >18 {{ saveMessage }}19 </div>20 <simple-table21 [columnResizing]="true"22 [rows]="rows"23 [columns]="headers"24 [height]="height"25 [theme]="theme"26 [onColumnWidthChange]="handleColumnWidthChange"27 ></simple-table>28 </div>29 `,30})31export class ColumnResizingDemoComponent implements OnInit {32 @Input() height: string | number = "400px";33 @Input() theme?: Theme;3435 readonly rows: Row[] = columnResizingData;36 headers: AngularColumnDef[] = [...columnResizingHeaders];37 saveMessage = "";3839 handleColumnWidthChange = (updatedHeaders: AngularColumnDef[]) => {40 try {41 const widthMap: Record<string, unknown> = {};42 for (const h of updatedHeaders) widthMap[h.accessor] = h.width;43 localStorage.setItem(COLUMN_RESIZING_STORAGE_KEY, JSON.stringify(widthMap));44 this.headers = updatedHeaders;45 this.saveMessage = "Column widths saved!";46 setTimeout(() => { this.saveMessage = ""; }, 2000);47 } catch {48 this.saveMessage = "Failed to save widths";49 setTimeout(() => { this.saveMessage = ""; }, 2000);50 }51 };5253 ngOnInit() {54 try {55 const saved = localStorage.getItem(COLUMN_RESIZING_STORAGE_KEY);56 if (saved) {57 const widthMap = JSON.parse(saved) as Record<string, number | string | undefined>;58 this.headers = columnResizingHeaders.map((h) => ({59 ...h,60 width: widthMap[h.accessor] ?? h.width,61 }));62 }63 } catch { /* ignore */ }64 }65}666768// column-resizing.demo-data.ts69// Self-contained demo table setup for this example.70import type { AngularColumnDef } from "@simple-table/angular";717273export const COLUMN_RESIZING_STORAGE_KEY = "columnResizingDemo_widths";7475export const columnResizingHeaders: AngularColumnDef[] = [76 { accessor: "id", label: "ID", width: 60, type: "number" },77 { accessor: "name", label: "First Name", width: "1fr", minWidth: 100, type: "string" },78 { accessor: "age", label: "Age", width: "1fr", minWidth: 50, type: "string" },79 { accessor: "role", label: "Role", width: 150, align: "right", type: "number" },80 { accessor: "department", label: "Department", width: "1fr", minWidth: 100, type: "string" },81 { accessor: "startDate", label: "Start Date", width: 150, type: "date" },82];8384export const columnResizingData = [85 { id: 1, name: "Dr. Marina Silva", age: 38, role: "Marine Biologist", department: "Research", startDate: "2019-03-15" },86 { id: 2, name: "Captain Alex Torres", age: 45, role: "Research Vessel Captain", department: "Operations", startDate: "2017-08-20" },87 { id: 3, name: "Dr. Coral Chen", age: 34, role: "Oceanographer", department: "Research", startDate: "2020-01-12" },88 { id: 4, name: "Finn O'Brien", age: 27, role: "Research Assistant", department: "Research", startDate: "2022-06-08" },89 { id: 5, name: "Reef Nakamura", age: 31, role: "Dive Safety Officer", department: "Safety", startDate: "2021-02-14" },90 { id: 6, name: "Tide Rodriguez", age: 29, role: "Equipment Specialist", department: "Technical", startDate: "2021-09-03" },91 { id: 7, name: "Dr. Ocean Williams", age: 42, role: "Research Director", department: "Leadership", startDate: "2016-05-10" },92 { id: 8, name: "Wave Petrov", age: 26, role: "Data Analyst", department: "Analysis", startDate: "2022-11-22" },93 { id: 9, name: "Pearl Kim", age: 33, role: "Laboratory Manager", department: "Laboratory", startDate: "2020-07-18" },94 { id: 10, name: "Current Hassan", age: 28, role: "Field Coordinator", department: "Operations", startDate: "2021-12-05" },95 { id: 11, name: "Abyss Thompson", age: 30, role: "ROV Operator", department: "Technical", startDate: "2021-04-20" },96 { id: 12, name: "Dr. Depth Martinez", age: 39, role: "Senior Researcher", department: "Research", startDate: "2018-10-14" },97];9899export const columnResizingConfig = {100 headers: columnResizingHeaders,101 rows: columnResizingData,102} as const;103
Svelte
Copy
1<script lang="ts">2 import { SimpleTable } from "@simple-table/svelte";3 import type { Theme, SvelteColumnDef } from "@simple-table/svelte";4 import { columnResizingHeaders, columnResizingData, COLUMN_RESIZING_STORAGE_KEY } from "./column-resizing.demo-data";5 import "@simple-table/svelte/styles.css";67 let { height = "400px", theme }: { height?: string | number; theme?: Theme } = $props();89 let headers: SvelteColumnDef[] = $state([...columnResizingHeaders]);10 let saveMessage = $state("");1112 $effect(() => {13 try {14 const saved = localStorage.getItem(COLUMN_RESIZING_STORAGE_KEY);15 if (saved) {16 const widthMap = JSON.parse(saved);17 headers = columnResizingHeaders.map((h) => ({ ...h, width: widthMap[h.accessor] ?? h.width }));18 }19 } catch { /* ignore */ }20 });2122 function handleColumnWidthChange(updatedHeaders: SvelteColumnDef[]) {23 try {24 const widthMap: Record<string, unknown> = {};25 for (const h of updatedHeaders) widthMap[h.accessor] = h.width;26 localStorage.setItem(COLUMN_RESIZING_STORAGE_KEY, JSON.stringify(widthMap));27 headers = updatedHeaders;28 saveMessage = "Column widths saved!";29 setTimeout(() => { saveMessage = ""; }, 2000);30 } catch {31 saveMessage = "Failed to save widths";32 setTimeout(() => { saveMessage = ""; }, 2000);33 }34 }35</script>3637<div style="position: relative; height: 100%">38 {#if saveMessage}39 <div style="position: absolute; top: 8px; right: 8px; background: #10b981; color: white; padding: 8px 16px; border-radius: 6px; font-size: 14px; font-weight: 500; z-index: 1000; box-shadow: 0 2px 8px rgba(0,0,0,0.15);">40 {saveMessage}41 </div>42 {/if}43 <SimpleTable44 columnResizing={true}45 columns={headers}46 rows={columnResizingData}47 {height}48 {theme}49 onColumnWidthChange={handleColumnWidthChange}50 />51</div>
Solid TSX
Copy
1import { createSignal, onMount, onCleanup } from "solid-js";2import { SimpleTable } from "@simple-table/solid";3import type { Theme, SolidColumnDef } from "@simple-table/solid";4import { columnResizingHeaders, columnResizingData, COLUMN_RESIZING_STORAGE_KEY } from "./column-resizing.demo-data";5import "@simple-table/solid/styles.css";67export default function ColumnResizingDemo(props: { height?: string | number; theme?: Theme }) {8 const [headers, setHeaders] = createSignal([...columnResizingHeaders]);9 const [saveMessage, setSaveMessage] = createSignal("");1011 let messageTimer: ReturnType<typeof setTimeout> | undefined;1213 const clearMessageTimer = () => {14 if (messageTimer !== undefined) {15 clearTimeout(messageTimer);16 messageTimer = undefined;17 }18 };1920 onMount(() => {21 try {22 const saved = localStorage.getItem(COLUMN_RESIZING_STORAGE_KEY);23 if (saved) {24 const widthMap = JSON.parse(saved) as Record<string, number | string | undefined>;25 setHeaders(26 columnResizingHeaders.map((h) => ({ ...h, width: widthMap[h.accessor] ?? h.width })),27 );28 }29 } catch {30 /* ignore */31 }32 });3334 onCleanup(() => {35 clearMessageTimer();36 });3738 const handleColumnWidthChange = (updatedHeaders: SolidColumnDef[]) => {39 try {40 const widthMap: Record<string, unknown> = {};41 for (const h of updatedHeaders) widthMap[h.accessor] = h.width;42 localStorage.setItem(COLUMN_RESIZING_STORAGE_KEY, JSON.stringify(widthMap));43 setHeaders(updatedHeaders);44 setSaveMessage("Column widths saved!");45 clearMessageTimer();46 messageTimer = setTimeout(() => setSaveMessage(""), 2000);47 } catch {48 setSaveMessage("Failed to save widths");49 clearMessageTimer();50 messageTimer = setTimeout(() => setSaveMessage(""), 2000);51 }52 };5354 return (55 <div style={{ position: "relative", height: "100%" }}>56 {saveMessage() && (57 <div58 style={{59 position: "absolute",60 top: "8px",61 right: "8px",62 background: "#10b981",63 color: "white",64 padding: "8px 16px",65 "border-radius": "6px",66 "font-size": "14px",67 "font-weight": "500",68 "z-index": 1000,69 "box-shadow": "0 2px 8px rgba(0,0,0,0.15)",70 }}71 >72 {saveMessage()}73 </div>74 )}75 <SimpleTable76 columnResizing77 columns={headers()}78 rows={columnResizingData}79 height={props.height ?? "400px"}80 theme={props.theme}81 onColumnWidthChange={handleColumnWidthChange}82 />83 </div>84 );85}
TypeScriptColumnResizingDemo.ts
Copy
1import { SimpleTableVanilla } from "simple-table-core";2import type { Theme, ColumnDef } from "simple-table-core";3import { columnResizingHeaders, columnResizingData, COLUMN_RESIZING_STORAGE_KEY } from "./column-resizing.demo-data";4import "simple-table-core/styles.css";56export function renderColumnResizingDemo(7 container: HTMLElement,8 options?: { height?: string | number; theme?: Theme },9): SimpleTableVanilla {10 const wrapper = document.createElement("div");11 wrapper.style.position = "relative";12 wrapper.style.height = "100%";1314 const toast = document.createElement("div");15 Object.assign(toast.style, {16 position: "absolute", top: "8px", right: "8px", background: "#10b981", color: "white",17 padding: "8px 16px", borderRadius: "6px", fontSize: "14px", fontWeight: "500",18 zIndex: "1000", boxShadow: "0 2px 8px rgba(0,0,0,0.15)", display: "none",19 });20 wrapper.appendChild(toast);2122 const tableContainer = document.createElement("div");23 wrapper.appendChild(tableContainer);24 container.appendChild(wrapper);2526 let headers: ColumnDef[] = [...columnResizingHeaders];27 try {28 const saved = localStorage.getItem(COLUMN_RESIZING_STORAGE_KEY);29 if (saved) {30 const widthMap = JSON.parse(saved);31 headers = columnResizingHeaders.map((h) => ({ ...h, width: widthMap[h.accessor] ?? h.width }));32 }33 } catch { /* ignore */ }3435 const table = new SimpleTableVanilla(tableContainer, {36 columns: headers,37 rows: columnResizingData,38 height: options?.height ?? "400px",39 theme: options?.theme,40 columnResizing: true,41 onColumnWidthChange: (updatedHeaders: ColumnDef[]) => {42 try {43 const widthMap: Record<string, unknown> = {};44 for (const h of updatedHeaders) widthMap[h.accessor] = h.width;45 localStorage.setItem(COLUMN_RESIZING_STORAGE_KEY, JSON.stringify(widthMap));46 toast.textContent = "Column widths saved!";47 toast.style.display = "block";48 setTimeout(() => { toast.style.display = "none"; }, 2000);49 } catch {50 toast.textContent = "Failed to save widths";51 toast.style.display = "block";52 setTimeout(() => { toast.style.display = "none"; }, 2000);53 }54 },55 });5657 return table;58}596061// column-resizing.demo-data.ts62// Self-contained demo table setup for this example.63import type { ColumnDef } from "simple-table-core";646566export const COLUMN_RESIZING_STORAGE_KEY = "columnResizingDemo_widths";6768export const columnResizingHeaders: ColumnDef[] = [69 { accessor: "id", label: "ID", width: 60, type: "number" },70 { accessor: "name", label: "First Name", width: "1fr", minWidth: 100, type: "string" },71 { accessor: "age", label: "Age", width: "1fr", minWidth: 50, type: "string" },72 { accessor: "role", label: "Role", width: 150, align: "right", type: "number" },73 { accessor: "department", label: "Department", width: "1fr", minWidth: 100, type: "string" },74 { accessor: "startDate", label: "Start Date", width: 150, type: "date" },75];7677export const columnResizingData = [78 { id: 1, name: "Dr. Marina Silva", age: 38, role: "Marine Biologist", department: "Research", startDate: "2019-03-15" },79 { id: 2, name: "Captain Alex Torres", age: 45, role: "Research Vessel Captain", department: "Operations", startDate: "2017-08-20" },80 { id: 3, name: "Dr. Coral Chen", age: 34, role: "Oceanographer", department: "Research", startDate: "2020-01-12" },81 { id: 4, name: "Finn O'Brien", age: 27, role: "Research Assistant", department: "Research", startDate: "2022-06-08" },82 { id: 5, name: "Reef Nakamura", age: 31, role: "Dive Safety Officer", department: "Safety", startDate: "2021-02-14" },83 { id: 6, name: "Tide Rodriguez", age: 29, role: "Equipment Specialist", department: "Technical", startDate: "2021-09-03" },84 { id: 7, name: "Dr. Ocean Williams", age: 42, role: "Research Director", department: "Leadership", startDate: "2016-05-10" },85 { id: 8, name: "Wave Petrov", age: 26, role: "Data Analyst", department: "Analysis", startDate: "2022-11-22" },86 { id: 9, name: "Pearl Kim", age: 33, role: "Laboratory Manager", department: "Laboratory", startDate: "2020-07-18" },87 { id: 10, name: "Current Hassan", age: 28, role: "Field Coordinator", department: "Operations", startDate: "2021-12-05" },88 { id: 11, name: "Abyss Thompson", age: 30, role: "ROV Operator", department: "Technical", startDate: "2021-04-20" },89 { id: 12, name: "Dr. Depth Martinez", age: 39, role: "Senior Researcher", department: "Research", startDate: "2018-10-14" },90];9192export const columnResizingConfig = {93 headers: columnResizingHeaders,94 rows: columnResizingData,95} as const;96
Props
Column Resizing Configuration
| Property | Required | Description | Example |
|---|---|---|---|
Property | Required | Description | Example |
columnResizingboolean | Optional | Enables dragging header dividers to resize columns. Double-click a handle to auto-fit that column to its content. | |
onColumnWidthChange | Optional | Fires after resize or double-click auto-size with the updated column defs. Use it to persist widths. |