Groups
Bundle related Node.js trace steps - tables, charts and other spans - into a named, collapsible section with recount.addGroup.
A group span bundles related child spans into a named, collapsible section in the trace view. Use groups when you want to display multiple tables, charts, or other spans together under a single heading.
Basic usage
recount.addGroup('Energy plan comparison', (g) => {
g.span('table', 'Current plan', {
output: recount.tableFromObject({
data: { plan: 'Standard', rate: '$0.28/kWh', dailySupply: '$1.10' }
})
});
g.span('table', 'Recommended plan', {
output: recount.tableFromObject({
data: { plan: 'GreenSaver', rate: '$0.24/kWh', dailySupply: '$0.95' }
})
});
});The group renders as a collapsible accordion with its child spans indented inside. The group itself does not count against your span quota - only its child spans do.
recount.addGroup(name, builder, options?)
Creates a group span on the active trace. Must be called inside a startTrace callback.
| Parameter | Type | Description |
|---|---|---|
name | string | Display name for the group |
builder | (g: SpanGroup) => void | Callback to add child spans to the group |
options | GroupSpanOptions | Optional group-level options |
Options
| Option | Type | Description |
|---|---|---|
description | string | Short description shown when the group is expanded in the dashboard (max 1,000 chars) |
limit | number | Keep only the first N child spans |
sample | number | Randomly sample down to N child spans (reservoir sampling, preserves relative order) |
sampleThreshold | number | Only apply sample if the child count exceeds this value. Defaults to sample |
limit and sample are mutually exclusive - if both are set, sample takes precedence.
Limiting child spans
Use limit to keep only the first N child spans. Useful when iterating over a list but only the leading results matter.
recount.addGroup(
'Search results',
(g) => {
for (const result of results) {
g.span('json', result.title, { output: result });
}
},
{ limit: 10 }
);Sampling child spans
Use sample to keep a random subset of N child spans. Selected spans preserve their original relative order.
recount.addGroup(
'Processed items',
(g) => {
for (const item of items) {
g.span('json', item.id, { output: item });
}
},
{ sample: 20 }
);Use sampleThreshold to only apply sampling when the child count exceeds a given value - useful when the list size varies and you only want to cut down large batches.
// Sample to 20 only if more than 100 items - otherwise keep all
recount.addGroup(
'Processed items',
(g) => {
for (const item of items) {
g.span('json', item.id, { output: item });
}
},
{ sample: 20, sampleThreshold: 100 }
);SpanGroup
The g object passed to the builder callback is a SpanGroup. It exposes the same span(), addMessage(), and alert() methods as the top-level trace - except you cannot nest groups within groups.
g.span(type, name, options?)
Add a child span to the group. Supports all non-group span types.
recount.addGroup('LLM pipeline', (g) => {
g.span('llm', 'Classify intent', {
input: { messages: [{ role: 'user', content: 'My bill seems too high' }] },
output: { response: 'billing_query' }
});
g.span('db', 'Fetch account history', {
metadata: { table: 'transactions', queryType: 'sql' },
input: {
query: 'SELECT * FROM transactions WHERE account_id = $1',
params: [42]
}
});
g.span('json', 'Build response', {
output: { recommendation: 'Switch to GreenSaver', savings: '$18.40/mo' }
});
});g.addMessage(name, text?, opts?)
Shorthand for adding a message child span.
recount.addGroup('Validation', (g) => {
g.addMessage('Policy check', 'Policy is active and in-force');
g.addMessage('Coverage check', 'Comprehensive cover confirmed');
g.addMessage('Excess check', 'Excess of $500 applies', { level: 'warn' });
});g.alert(label, level, options?)
Flags the most recently added child span with a warn or error badge. See Alerts for the full API.
recount.addGroup('Risk checks', (g) => {
g.span('json', 'Credit check', { output: { score: 610 } });
if (creditScore < 650) {
g.alert('Below minimum credit score', 'error', {
checks: [{ value: 610 }]
});
}
});Using the SpanGroup class directly
If you need to pass the group builder around or construct it separately, import SpanGroup:
import recount, { SpanGroup } from '@refactorlabs/recount';
function buildComparisonGroup(g: SpanGroup, plans: Plan[]) {
for (const plan of plans) {
g.span('table', plan.name, {
output: recount.tableFromObject({ data: plan })
});
}
}
await recount.startTrace('Compare plans', async () => {
recount.addGroup('Plan options', (g) => buildComparisonGroup(g, plans));
});