Go somewhere.

Home Page About Page Projects Page CV Page Dev Page Journal Page Contact Page Astro for Documentation and a Professional Site Dev Codex anywhere with tmux, Mosh, Termius, and Tailscale Dev Observing Codex MCP Tool Calls with Langfuse Dev Localization in Product Apps Dev MCP as a Safe AI Integration Boundary Dev Zod, OpenAPI, and Swagger for API Contracts Dev pg-boss for Durable Background Jobs Dev pgvector and RAG, Explained Through a Real Knowledge Workflow Dev Pragmatic Drag and Drop for Real Ordering Tasks Dev Prisma and PostgreSQL as the Product Source of Truth Dev Ralph Loop as a Plan Queue Dev React Router for Full-Stack Product Workflows Dev shadcn-Style UI as an Owned Product System Dev Dense Operational UI with Tables and Editors Dev Terminal Spike: a Native Android Terminal Dev Input Chinese pinyin with tones on Linux with fcitx Dev How to configure the DEFT Pro trackball on Linux Dev Use Pocket (read it later) on KOReader Dev Vercel AI SDK with Explicit Tool Boundaries Dev Vertical Slice Architecture with Dependency-Cruiser Dev Testing Product Workflows with Vitest and Playwright Dev Zod Beyond Validation Dev The Brothers Karamazov is the best novel I have ever read Journal Oral history of two Christians in a Chinese labour camp in the 1960s Journal The Word in the Bible Journal We love because He first loved us, not meaningless self-love Journal Mom, let me take the blame for Dad's mistakes Journal My story with God Journal The world is far from God, close to China Journal The Five Love Languages and Extrovert Only Exist in Language Journal Personality types don't exist, life is not a matching game Journal Dangerous words, why psychology is impossible Journal The story of a Shenzhen worker in 2000 Journal A Conversation with a Driver in Ras Al Khaimah Journal Shadian: a 1975 conflict between Communist forces and Muslims in China Journal Documentaries About China Journal
← Dev/Engineering

Dense Operational UI with Tables and Editors

Why some product workflows need dense tables, dirty-state tracking, and careful batch saves.

Sometimes a simple form is the wrong UI. If the user needs to compare many values and make careful edits, a table can be kinder than a long page of inputs.

Dense UI has a bad reputation when it is used to hide messy thinking. But some work is naturally dense. A user may need to review many rows, compare columns, edit a few values, and then save everything as one deliberate batch.

In that workflow, the table is not decoration. It is the workspace. If I reach for a table helper, I want something like TanStack Table to stay headless enough that the product still owns the editing model.

The useful details are small:

Rows changed: 4
Invalid rows: 1
Save button: disabled until errors are fixed

Changed cells should be visible. Invalid cells should point to the problem. The user should know whether they are looking at saved data, unsaved edits, or a failed save.

I like batch save for this kind of workflow because it gives the user a review moment. Immediate save can be good for simple settings, but it can be risky when the user is editing several related values. A batch save lets the app validate the whole change before committing it.

The hard part is state. A table editor needs to know:

  • the original value
  • the current edited value
  • whether the cell is dirty
  • whether the row is valid
  • whether the save is in progress
  • what failed if the save did not work

If that state is vague, users lose trust. They start asking whether the app saved their work. That is a product failure, not just a UI bug.

Keyboard flow also matters. If the user is editing repeated values, mouse-only interaction becomes slow. Tab, Enter, Escape, copy, paste, and predictable focus can make the difference between a tool that feels usable and a tool that feels decorative.

For more advanced editors, the same principle applies. A rule editor or structured text editor, whether built on CodeMirror or another editor component, should not just accept text and hope for the best. It should help the user understand valid structure, show errors before execution, and make risky actions explicit.

The trade-off is complexity. A dense table can become a small application inside the application. It needs accessibility review, performance care, careful validation, and test coverage around dirty state. If the workflow only edits one or two fields, a normal form is probably better.

I would choose a dense table when:

  • comparison across rows is part of the task
  • users edit more than one item at a time
  • validation depends on the whole batch
  • keyboard speed matters
  • mistakes are costly enough to justify a review step

I would avoid it when a simple form would be clearer. More cells do not automatically mean more power.

The goal is not to fit more data on screen. The goal is to reduce mistakes for users whose work already has many details.