What a security analyst needs from a data table
Topics: design · ux · tables
A security analyst does not use a table like a report. They search for a value, turn it into a filter, compare the time of one event with another, open the details, and then return to the row where the question started. If the table forgets that path at every step, it becomes part of the problem.
That is why I do not start with the visual design of the columns. I start with the question the analyst is trying to answer, then arrange the screen around that decision.
Density is not automatically bad
In a consumer product, generous space often makes a page easier to understand. In an investigation screen, too much space can make comparison harder. If the analyst needs to see fifteen related events at once, 64-pixel rows force half the evidence out of view.
That does not mean compressing everything until it becomes unreadable. I use a compact row height, enough space to separate columns, and a monospaced face for time, IP addresses, hashes, and similar values. Colour is reserved for the state that deserves attention. If every column is coloured, no colour can signal anything.
Monospaced text earns its place here. Repeated values line up vertically, so the eye can scan a column without rereading every cell from the beginning.
Put columns in the order of the decision
There is no universal column order. One investigation may begin with severity; another begins with time, source, or the rule that produced the event. I ask which value the analyst checks first and which value makes them open the row.
Those columns belong near the start. Supporting details can follow, scroll horizontally, or remain hidden until requested.
Column customization is useful, but it is not an excuse for a weak default. Most people will not rebuild the table on their first day. The initial order should support the common investigation, then users who need another view can save it.
I also avoid freezing half the table. One or two fixed columns can preserve the row identity during horizontal scrolling. More than that leaves too little room for the data that changes.
The keyboard is part of the workflow
When someone moves through a large event set, repeated pointer travel becomes slower than the decision itself. The table should support arrow-key movement, opening details from the keyboard, and turning a cell value into a filter without making the user reach for the mouse every time.
The shortcut is only half the work. Focus must return to a sensible place afterward. If the analyst opens a row and closes it, focus should return to that row. If they add a filter, the browser should not drop focus at the top of the document.
I show focus and selection differently. Focus means the next keyboard action applies here. Selection means the row belongs to a bulk operation. A row can have both states, but they do not mean the same thing.
For a virtualized table, focus also affects implementation. A focused row cannot disappear from the accessibility tree merely because it moved one pixel outside the viewport. Overscan and focus management have to agree, or keyboard movement will jump unpredictably.
Returning should not mean starting over
An investigation state is more than one filter. It includes:
- filters;
- result order;
- visible columns;
- the open row;
- scroll position.
If the analyst opens a details page and comes back, the table should be where they left it. If they refresh the page or send the URL to a teammate, rebuilding the same query is useful.
I place filters, sorting, and the time range in the URL when possible. Temporary interface state, such as whether a side panel is expanded, can remain local. The goal is not to put every boolean into the address. It is to preserve the decisions that produced the current result.
A URL also makes a bug report much better. “This table is wrong” is difficult to reproduce. A link containing the time range and filters gives the developer the same starting point.
Filters should read like an explanation
Filter builders often become small programming languages. That power may be necessary, but the active state still needs to be readable.
I prefer visible clauses that can be inspected and removed one at a time:
severity is high
AND source.ip is 10.0.0.8
AND timestamp is within the last 30 minutesThe table should show how many filters are active and which one is responsible when the result set becomes empty. “No data” is ambiguous. It might mean nothing matched, the request failed, permissions removed a field, or the stream is not connected.
Saved views can help with repeated investigations, but they need names and ownership. A shared view changed without warning can alter how an entire team sees the same queue. I treat shared filters more like configuration than a personal preference.
Details should not erase the list
Opening a row is common enough that a full navigation can be expensive. A side panel or split view often works better: the list remains visible, the selected row remains clear, and the analyst can compare adjacent events without rebuilding context.
The panel should not contain a second unstructured dump of every field. I group information by the decisions it supports: what happened, where it came from, which entity is involved, and what can be done next. Raw data can remain available, but it should not be the only view.
When the details need a full route, I still preserve the list state in the URL or navigation history. The user should be able to move back without the table jumping to its initial state.
Live data needs a pause
A live table that reorders itself under the pointer is not “real time” in a useful sense. It is unstable.
I let the table follow new events while the analyst is at the live edge. Once they scroll or select a row, I hold the visible list steady and count new arrivals behind a control. The analyst can merge them when ready.
The same applies to sorting. If the active sort causes a row to move after an update, I avoid moving it while the person is reading or editing its details. A data table is not only a display of truth; it is also a workspace, and work needs a stable surface.
Empty, loading, and error are different states
Skeleton rows are useful during the first load. They are misleading after an error. An empty result needs the active filter summary. A disconnected stream needs the time of the last trusted event. These states require different copy and actions.
I try to answer the next question in each state:
- Loading: what is happening, and can the person leave?
- Empty: which query produced no result?
- Error: what failed, and is retry safe?
- Stale: how old is the data currently on screen?
This is where a table stops being a generic component. The columns may be reusable, but the state language belongs to the actual system.
The best security table is not the one with the most controls. It is the one that lets an analyst hold their place, ask another question, and return without losing the thread. Density, keyboard behavior, URL state, and stable live updates all serve that one goal.