Pin columns and restore their layout
Pin Name left and Status right, then check horizontal scrolling, a narrow container, and layout restoration.
Steps to try
Section titled “Steps to try”- Scroll the horizontal scrollbar right. Name and Status stay at their respective edges while Team, Email, and Region scroll.
- Select Save layout → Unpin columns. All columns enter the center zone. Restore layout brings the pins back.
- Select 260px width. When pinned widths exceed the available space, some pins temporarily enter the scrolling zone.
- Use Inspect configuration to confirm that
pinnedis retained. Return to Full width to recover effective pinning when space permits.
Configuration and persistence
Section titled “Configuration and persistence”| Value/API | Purpose |
|---|---|
columns[].pinned |
Sets initial "left" or "right" pinning. |
getColumnLayout() |
Reads current order, width, visibility, and pin configuration. |
setColumnLayout(layout) |
Reapplies that structure; it does not restore source row data. |
onChangeColumnLayout |
Updates the example’s displayed layout JSON. |
The save button keeps an in-memory snapshot which is lost on page reload. Use layouts compatible with the same column IDs. Reading and writing happen in separate user events.
Narrow containers
Section titled “Narrow containers”The package reserves at least 48px for center content. If pins exceed that budget, it temporarily demotes inner blocks from the wider side; ties prefer the right side. This example uses 140px left and 130px right. At 260px, Name can lose its effective pin while its saved pinned: "left" configuration remains intact.
Demotion does not change configuration or emit onChangeColumnLayout. The direct resize limit of a pinned column differs from temporary demotion on container resize.
Scope and related documentation
Section titled “Scope and related documentation”This example covers individual flat-data Header/Body columns. It does not verify all Header Group, Summary boundary, resize gesture, or Tree combinations. Column Pinning is unavailable in Tree mode.
Column pinning guide · getColumnLayout · setColumnLayout
Try it live
Section titled “Try it live”import { useRef, useState } from 'react';import { CominsTable, type CominsTableColumn, type CominsTableRef, type CominsColumnLayout } from 'comins-table';import 'comins-table/styles.css';type Row = { id: string; name: string; team: string; email: string; region: string; status: string };const rows: Row[] = [ { id: 'alpha', name: 'Alpha', team: 'Design', email: 'alpha@example.test', region: 'Seoul', status: 'Active' }, { id: 'beta', name: 'Beta', team: 'Engineering', email: 'beta@example.test', region: 'Busan', status: 'Pending' },];const columns: CominsTableColumn<Row>[] = [ { id: 'name', field: 'name', label: 'Name', width: 140, pinned: 'left' }, { id: 'team', field: 'team', label: 'Team', width: 200 }, { id: 'email', field: 'email', label: 'Email', width: 260 }, { id: 'region', field: 'region', label: 'Region', width: 200 }, { id: 'status', field: 'status', label: 'Status', width: 130, pinned: 'right' },];export default function TablePinningDemo({ lang = 'ko' }: { lang?: string }) { const ko = lang === 'ko'; const table = useRef<CominsTableRef<Row>>(null); const [saved, setSaved] = useState<CominsColumnLayout | null>(null); const [layout, setLayout] = useState<CominsColumnLayout | null>(null); const [narrow, setNarrow] = useState(false); return <div className="demo" data-demo="table-pinning"> <div className="demo-toolbar"> <button onClick={() => { const next = table.current?.getColumnLayout(); if (next) { setSaved(next); setLayout(next); } }}>{ko ? '배치 저장' : 'Save layout'}</button> <button onClick={() => { const next = table.current?.getColumnLayout(); if (next) table.current?.setColumnLayout({ ...next, columns: Object.fromEntries(Object.entries(next.columns).map(([id, column]) => [id, { ...column, pinned: undefined }])) }); }}>{ko ? '고정 해제' : 'Unpin columns'}</button> <button disabled={!saved} onClick={() => { if (saved) table.current?.setColumnLayout(saved); }}>{ko ? '저장 배치 복원' : 'Restore layout'}</button> <button onClick={() => setNarrow(value => !value)}>{narrow ? (ko ? '원래 폭' : 'Full width') : (ko ? '260px 폭' : '260px width')}</button> <button onClick={() => setLayout(table.current?.getColumnLayout() ?? null)}>{ko ? '설정 조회' : 'Inspect configuration'}</button> </div> <div className="table-viewport" style={{ width: narrow ? 260 : '100%', maxWidth: '100%' }}> <CominsTable ref={table} columns={columns} data={rows} getRowId={row => row.id} onChangeColumnLayout={setLayout} /> </div> <p>{ko ? '아래 가로 스크롤바를 움직여 Name과 Status 위치를 비교하십시오.' : 'Use the horizontal scrollbar below the table to compare Name and Status positions.'}</p> <details><summary>{ko ? '설정된 컬럼 배치' : 'Configured column layout'}</summary><pre data-pinning-layout>{JSON.stringify(layout, null, 2)}</pre></details> </div>;}