Table row-click drilldowns
A table tile is often a catalog: one row per service, host, endpoint, or error group, with a few columns that score each one. Row-click actions turn that catalog into an inspection workflow. You scan the catalog to find the row that matters, click it, and ClickStack carries the clicked row's values through as filters, so the destination opens already scoped to that one item with no query to rebuild by hand.
A click can land in one of two places:
- another dashboard, for a focused view of the one item, such as a per-service detail dashboard, or
- the underlying events in Search, for the logs or traces behind the row.
Both use cases below start from the same catalog, a service inventory, and drill from it into each destination. Row-click actions apply to table tiles only. They are distinct from the chart drilldown to search, which opens a context menu when you click a point on a line or bar chart.
Inspect a service in its own dashboard
An overview table with one row per service answers "which service is unhealthy?". A row click can open a per-service dashboard that answers "what is happening inside it?", scoped to the service you clicked. The pattern below pairs a service inventory table with a Service Detail dashboard.
Build the detail dashboard
Create a dashboard named Service Detail and add a custom filter with the expression ServiceName on your traces source. The dashboard-level filter re-scopes every tile to a single service, so the tiles themselves do not hardcode a service in their queries. Add the per-service views you want: RED key figures (requests, errors, P95 duration), a latency-percentile chart (P50, P95, P99), request rate over time, and a per-endpoint breakdown grouped by SpanName.
Save this dashboard first so you can select it as the target in the next step.
Build the service inventory
On an overview dashboard, add a Table tile on your traces source grouped by ServiceName. Give it the RED columns that score each service, each one an aliased series:
Requests: count of spans (rate).Errors: count of spans with an error status.P95 Duration: the 95th percentile ofDuration. Set the column's number format to a duration so it reads as288ms, not raw nanoseconds.
Order by Requests descending so the busiest services sort to the top. This table is the catalog: one row per service, scored by RED.
By default a table renders its group-by column (here ServiceName) on the right, after the series. A catalog reads better with each row's identity first. Open the tile's Display Settings and turn on Display Group By Columns on Left to move the group-by column to the front.
Wire up the row click
On the inventory table, open Row Click Action, select Dashboard, and choose Service Detail from the dashboard list. Picking the dashboard directly pins it by ID, so the link keeps working if the dashboard is renamed later, and it survives dashboard export and import. (Reserve the Template option for choosing a different dashboard per row; see Set up a row-click action.)
Because Service Detail declares a ServiceName custom filter, the drawer pre-fills an empty filter for that expression. Fill in its template:
- Expression:
ServiceName(already filled in) - Template:
{{ServiceName}}
Click Apply and save. The clicked row's service now flows into the dashboard's ServiceName filter.
Click a row
Hovering a row reveals a link affordance on the right edge of the table, with a hint describing the action (Open dashboard "Service Detail"). Clicking the row opens the Service Detail dashboard with its Service filter set to the clicked value, so every tile (the RED key figures, the latency percentiles, the per-endpoint breakdown) re-scopes to that one service in a single click.
Jump from a service to its traces
Sometimes you do not want another aggregate view, you want the raw events. A Search action sends the click to the Search page instead of a dashboard, opening the logs or traces behind the row, already filtered to it.
Starting from the same service inventory table, point its row click at the traces themselves rather than the detail dashboard.
Point the row click at Search
On the inventory table, open Row Click Action and select Search. Choose your traces source (only log and trace sources are listed). Add one filter:
- Expression:
ServiceName - Template:
{{ServiceName}}
Click Apply and save.
Click a row
Clicking a service row now opens the Search page on that traces source, filtered to ServiceName = <service>, so you land on the spans for just that service over the same time range.
The same shape works for any catalog of items. Group the table by an operation (SpanName) or an endpoint attribute instead of ServiceName, template the filter from that column, and each row click opens the events for that one operation or endpoint. For a group-by on a map attribute, see the alias note under Validation and limitations.
Set up a row-click action
Row-click actions are configured on the table tile itself; there is no separate page for them. Add or edit a tile and set its visualization type to Table. A Row Click Action button then appears in the editor toolbar, next to Display Settings. The button is shown for table tiles only, and its label reflects the current action: Row Click Action: Default, Row Click Action: Search, or Row Click Action: Dashboard. Click it to open the drawer.
The drawer offers three actions:
- Default: the built-in behavior. Clicking a row opens the Search page, filtered by the row's group-by column values and the selected time range. This is what you get when no custom action is set.
- Search: send the click to the Search page for a source you choose.
- Dashboard: send the click to another dashboard owned by your team.
For Search and Dashboard, you choose where the click lands and template the filters carried into it:
- Destination: pick a specific source or dashboard, or choose Template and enter a Handlebars template that is matched by name to an available source or dashboard. Picking a specific target pins it by ID; prefer this for a single fixed destination, since it survives renames and dashboard export and import, and for a dashboard it pre-fills the destination's declared filters. Use Template when the target should vary per row, referencing a row column to choose it (for example
Errors-{{ServiceName}}). - Filters: click Add filter and provide an Expression (a column or expression on the destination, for example
ServiceName) and a Template for its value (for example{{ServiceName}}). Templates reference the clicked row's columns with{{columnName}}(see the note below on which columns are available). Each filter renders to anexpression IN (value)condition at the destination, and filters that share an expression are merged. When the destination is a dashboard, the drawer pre-fills one empty filter for each filter that dashboard already declares, so you only fill in the templates. - WHERE (optional): a Handlebars template rendered into the destination's global filter, in addition to the per-filter conditions above. Set its query language to SQL or Lucene so the destination parses it. For example, the SQL template
ServiceName = '{{ServiceName}}'scopes the destination to the clicked row's service.
A {{...}} template resolves against the table tile's own columns: the group-by columns and each series by its name or alias, exactly as they appear in the table. It does not see columns of the underlying source that the table does not select. A value has to be a column in the table to be carried through a click, so {{ServiceName}} works because ServiceName is the table's group-by column, and an aliased column is referenced by its alias. Referencing a name the table does not have fails the click with Row has no column '<name>'.
Click Apply to validate the templates (ClickStack reports any with invalid syntax), then save the dashboard to persist the action.
How the destination and filters resolve
When a viewer clicks a row, ClickStack resolves the action against that row:
- Destination. Choosing a specific source or dashboard pins it by ID. A Template target renders against the clicked row and is then matched by name against your team's sources or dashboards. A name must be unique to resolve: if two sources or two dashboards share the rendered name, the link cannot resolve to one of them. An empty rendered name, or a name with no match, also fails to resolve.
- Filters. Each filter template renders against the row and becomes an
expression IN (value)condition at the destination. ASearchaction opens/searchscoped to the chosen source; aDashboardaction opens that dashboard. The clicked row's time range carries over in both cases.
A filter carried into a dashboard only takes effect if the destination dashboard declares a top-level custom filter whose expression matches the filter's Expression. If no declared filter matches, that value is dropped at click time and the destination opens unfiltered for that expression. This is why the dashboard mode pre-fills the destination's declared filters: match the expression, and the destination dropdown auto-populates with the clicked row's value.
Validation and limitations
- Table tiles only. The Row Click Action button appears for table tiles, both the chart-builder table and a SQL-based table. Other tile types do not have a row-click action.
- Search targets must be log or trace sources. Metric and session sources are not offered, since they cannot be viewed on the search page.
- Template names must be unique. A template target resolves by name, so two sources or two dashboards with the same name cannot be used as a template target.
- Dashboard targets need a matching declared filter for a carried value to take effect (see the note above).
- Expression group-by columns need an alias. A group-by on an expression, such as a map attribute
SpanAttributes['http.route'], produces a result column whose name is the raw expression, which is awkward to reference in a template. Give the column an alias in the chart builder: addAS <alias>after the expression in the Group By input (for exampleSpanAttributes['http.route'] AS Route), then reference{{Route}}in the template. Aggregated series take an alias from their Alias field the same way. A group-by on a plain column such asServiceNamealready has a clean name and needs no alias.