Stack Trace Viewer
A stack trace viewer that lists each frame, highlights your own code while folding library frames, expands a frame into its source snippet, and copies the trace in one click.
A stack trace viewer for AI coding assistants, debugging panels and error report pages. Pass structured frames, or hand a raw stack string to raw — the built-in parseStack understands the two common shapes, at fn (file:line:col) and fn@file:line:col, and keeps any line it cannot parse as plain text. Your own code is marked with a left accent band and a "Your code" tag, while consecutive library frames fold into a single "⋯ 3 library frames" row. Expanding a frame reveals its source snippet, with the failing line tinted and a ^ marker under the column. The button in the top-right corner copies the whole trace, as raw text or Markdown.
Bring this into your project
Run this command in your project folder. The CLI adds the source and dependencies; then follow the usage example below.
npx webberui@latest add stack-trace-viewer.enPlayground
Tune the props live — the code snippet updates as you go, so you can dial in the look you want before copying it.
<StackTraceViewer/>
The complete example includes the preview layout and data. Install the component first, then paste this file into your project.
Installation
npx webberui@latest add https://webberui.com/r/stack-trace-viewer.en.jsonOr, once registries are configured in components.json, install it as @webberui/stack-trace-viewer.
Usage
import { StackTraceViewer } from "@/components/ui/stack-trace-viewer";
// Pass a raw stack string
<StackTraceViewer
error={{ name: err.name, message: err.message }}
raw={err.stack}
onFrameClick={(frame) => openInEditor(frame.file, frame.line)}
/>
// Or pass structured frames (with optional source snippets)
<StackTraceViewer
error={{ name: "TypeError", message: "Cannot read properties of undefined (reading 'amount')" }}
frames={[
{
fn: "formatPrice",
file: "src/lib/price.ts",
line: 42,
column: 29,
snippet: { startLine: 41, lines: [" const rate = item.taxRate ?? 0.05;", " const off = item.discount.amount;"] },
},
{ fn: "beginWork", file: "node_modules/ui-runtime/dist/render.js", line: 2210, column: 14 },
]}
copyFormat="markdown"
/>Props
| Prop | Type | Default | Description |
|---|---|---|---|
error | StackTraceError | — | Error header: type and message |
frames | StackFrame[] | — | Structured stack frames; take precedence over raw |
raw | string | — | Raw stack text, parsed with the built-in parseStack |
isUserFrame | (frame: StackFrame) => boolean | see description | Decides whether a frame is user code; by default the file must not contain node_modules, start with node:, or contain internal/ |
defaultExpanded | "first-user" | "none" | "all" | "first-user" | Initial expansion in uncontrolled mode: open the first user frame / collapse all / expand all |
expanded | string[] | — | Controlled list of expanded frame ids; managed internally when omitted |
onExpandedChange | (ids: string[]) => void | — | Called when the expanded list changes |
collapseLibrary | boolean | true | Fold runs of consecutive library frames (two or more) into one row |
onFrameClick | (frame: StackFrame) => void | — | Called when a frame row is clicked (or activated with Enter/Space), e.g. to open it in an editor |
copyFormat | "raw" | "markdown" | "raw" | Format produced by the copy button |
maxHeight | number | string | 420 | Max height of the frame list before it scrolls internally; numbers are px |
duration | number | 180 | Expand animation duration (ms), also written to --wb-stack-trace-viewer-duration |
className | string | — | Passed through to the outermost container |
StackFrame
| Field | Type | Default | Description |
|---|---|---|---|
id | string | — | Stable identifier; generated from the index when omitted (frame-0, frame-1, …) — this is the value expanded uses |
fn | string | — | Function name; shows "(anonymous)" when missing |
file | string | — | File path |
line | number | — | Line number (1-based) |
column | number | — | Column (1-based), used to draw the ^ marker on the failing line |
isUser | boolean | — | Explicitly marks the frame as user code; falls back to isUserFrame when omitted |
snippet | StackFrameSnippet | — | Source snippet; the expanded area shows "No source available" when missing |
text | string | — | A raw line that could not be parsed (file is an empty string and line is 0); renders as a plain-text row |
StackFrameSnippet
| Field | Type | Default | Description |
|---|---|---|---|
startLine | number | — | Line number of the snippet's first line in the source file |
lines | string[] | — | Snippet content, one element per line |
StackTraceError
| Field | Type | Default | Description |
|---|---|---|---|
name | string | — | Error type, e.g. TypeError |
message | string | — | Error message; may span multiple lines |
The module also exports parseStack(raw), formatStackTrace(error, frames, format), formatFrameLocation(frame) and defaultIsUserFrame(frame), so your server or error-reporting pipeline can reuse the same parsing and formatting logic.
How it works
- Parsing: the first non-empty line is skipped when it is not a frame (usually the
TypeError: …header); any other unparseable line is kept as a plain-text frame instead of silently disappearing. - Folding: only runs of two or more library frames fold into one row — folding a lone frame saves no space. Once opened, the frames fade in with a 20ms stagger and focus moves to the first frame of the group.
- Path truncation: long paths truncate the directory part while the file name and line:col always stay visible, which amounts to a middle truncation; the full path is in the
titletooltip. - Source snippets are not syntax-highlighted and use only the neutral scale, so they never clash with the code theme your site already has.
- Copying: on success the icon cross-fades to a check for 1.5s. When the browser denies clipboard access, a read-only field opens with the text already selected and a prompt to press Ctrl+C / ⌘C.
- Timing variable:
--wb-stack-trace-viewer-duration(chevron rotation, default 180ms, written fromduration); override the user-frame band color with--wb-stack-trace-viewer-accent.
Accessibility
- The whole component is a
role="region"labelled "Error stack trace", and the frame list uses ordered-listolsemantics - Each frame is a native
buttonwitharia-expandedandaria-controls, and anaria-labelthat fully describes it, e.g. "Frame 3: CartSummary, at src/components/cart-summary.tsx line 27, your code" - Keyboard: the list uses a roving tabindex and takes a single Tab stop; ↑/↓ move between frames, Home/End jump to the ends, Enter/Space toggle, → expands, ← collapses, and C copies the focused frame's location (Ctrl/⌘+C is left to the browser)
- The fold rows and the copy button are native
buttons; copy results are announced as "Copied" througharia-live="polite" - Screen readers additionally hear "failing line" on the error line, while the decorative line-number gutter and ^ marker are hidden from assistive tech
- When the user has Reduce Motion turned on, panels appear instantly, fold rows expand without the stagger, and the copy icon swaps without a cross-fade
Agent Task Queue
An agent task queue React component with drag-to-reorder, a breathing pulse on the running item, and finished tasks sliding out into a collapsible Done group.
Subagent Status Card
A React subagent status card nested under its parent message, showing the task, a progress bar and current step, expandable to reveal its streamed output and collapsing to a one-line summary when done; a Group shares one connector line and staggers the cards in.