Skip to content

Pinned columns

Pinning freezes a column against a horizontal scroll so it stays visible while the rest of a wide table scrolls underneath — most often the group / label column, so you never lose track of which row you’re reading across many measure or window columns.

This is best seen in a running app with a horizontally-scrollable table. The wiring:

from dash_tensor_grid import TensorGrid, build_grid
payload = build_grid(sales, {
"rows": ["region", "country"],
"measures": {"revenue": "sum", "qty": "sum"},
"formats": {"revenue": "currency:USD", "qty": "number:0"},
})
# Pin a column by setting `pinned` on its column def: 'left' | 'right'.
for col in payload["column_defs"]:
if col.get("accessorKey") == "region":
col["pinned"] = "left"
TensorGrid(id="grid", row_data=payload["row_data"], column_defs=payload["column_defs"])
  • Dash — per-column, either edge. Set pinned: 'left' or pinned: 'right' on any column def. The selection column auto-pins left whenever anything is pinned, so checkboxes stay next to the frozen label. See the column-def reference.
  • Adapters — freeze the label column. pinFirstColumn sticks the group / dimension column via position: sticky and tags it tg-pinned. You provide the horizontally-scrollable wrapper (overflow-x: auto) and an opaque background for the pinned cells (e.g. .tg-grid .tg-pinned { background: #fff }) so scrolled content doesn’t bleed through and row hover / selection still shows.
  • Pinning is a display concern only — it never changes the data or the aggregation. Pair it with a column footer to keep both the label column and the totals row on screen.