Summary
Column visibility toggles in the dashboard reset on every page reload and on navigation away from the table. Hiding columns on Events, Workers, Workflows, Scheduled Runs, Recurring, Filters, Rate Limits, or Workflow Runs only sticks for the current mount of that page.
Repro
- Open
/events (or any other table page).
- Click the column-toggle menu, hide one or more columns.
- Hard reload, or navigate away and back.
- All columns are visible again.
Reproduced on a self-hosted instance running ghcr.io/hatchet-dev/hatchet/hatchet-dashboard:latest. Same code path applies to Hatchet Cloud since it ships the same React app.
Root cause
Every table page wires columnVisibility to plain useState, never to a persistence layer. Confirmed by code search across frontend/app/src/pages/main/v1/:
| File |
Line |
events/index.tsx |
59 |
workers/index.tsx |
45 |
scheduled-runs/index.tsx |
63 |
workflows/index.tsx |
19 |
filters/index.tsx |
41 |
recurring/index.tsx |
28 |
rate-limits/index.tsx |
24 |
workflow-runs-v1/hooks/runs-provider.tsx |
97 |
DataTable itself (frontend/app/src/components/v1/molecules/data-table/data-table.tsx) is correctly designed as a controlled component: it accepts columnVisibility and setColumnVisibility as props. The persistence responsibility sits with the parent, and no parent provides a persistent setter.
Suggested fix
The repo already has frontend/app/src/hooks/use-local-storage-state.tsx, which:
- reads the initial value from
window.localStorage,
- writes on every set,
- syncs across tabs via the native
storage event plus a custom hatchet:local-storage event for same-tab listeners,
- falls back gracefully on JSON parse / quota errors.
It is already used for sidebar collapse (use-sidebar.tsx), the refetch interval (refetch-interval-context.tsx), and the side panel (nav/side-panel.tsx).
Swapping useState<VisibilityState>(default) for useLocalStorageState<VisibilityState>(\"hatchet:columns:<page>\", default) at each of the 8 sites above should be enough. Each page would need a stable key, e.g. hatchet:columns:events, hatchet:columns:workers, etc.
Why it matters
Anyone who customizes their table view has to redo it every time they reload or change pages. The hook to fix it is in-tree, the change is mechanical, and no schema or backend work is involved. Happy to send a PR if the team is open to that approach.
Summary
Column visibility toggles in the dashboard reset on every page reload and on navigation away from the table. Hiding columns on Events, Workers, Workflows, Scheduled Runs, Recurring, Filters, Rate Limits, or Workflow Runs only sticks for the current mount of that page.
Repro
/events(or any other table page).Reproduced on a self-hosted instance running
ghcr.io/hatchet-dev/hatchet/hatchet-dashboard:latest. Same code path applies to Hatchet Cloud since it ships the same React app.Root cause
Every table page wires
columnVisibilityto plainuseState, never to a persistence layer. Confirmed by code search acrossfrontend/app/src/pages/main/v1/:events/index.tsxworkers/index.tsxscheduled-runs/index.tsxworkflows/index.tsxfilters/index.tsxrecurring/index.tsxrate-limits/index.tsxworkflow-runs-v1/hooks/runs-provider.tsxDataTableitself (frontend/app/src/components/v1/molecules/data-table/data-table.tsx) is correctly designed as a controlled component: it acceptscolumnVisibilityandsetColumnVisibilityas props. The persistence responsibility sits with the parent, and no parent provides a persistent setter.Suggested fix
The repo already has
frontend/app/src/hooks/use-local-storage-state.tsx, which:window.localStorage,storageevent plus a customhatchet:local-storageevent for same-tab listeners,It is already used for sidebar collapse (
use-sidebar.tsx), the refetch interval (refetch-interval-context.tsx), and the side panel (nav/side-panel.tsx).Swapping
useState<VisibilityState>(default)foruseLocalStorageState<VisibilityState>(\"hatchet:columns:<page>\", default)at each of the 8 sites above should be enough. Each page would need a stable key, e.g.hatchet:columns:events,hatchet:columns:workers, etc.Why it matters
Anyone who customizes their table view has to redo it every time they reload or change pages. The hook to fix it is in-tree, the change is mechanical, and no schema or backend work is involved. Happy to send a PR if the team is open to that approach.