Bug reporting for frontend developers — the 2026 playbook
Role-specific bug-reporting playbook for frontend developers: what to capture, how to file, and how to handoff cleanly to engineering — without bouncing tickets back.
Why Frontend Developers need a different playbook
Frontend developers debug a state they cannot see. The ticket says "the page looks broken," attaches a screenshot, and the screenshot shows pixels — not the DOM tree that produced them, not the console error that fired underneath, not the network waterfall that fed the component the wrong data. The bugs that eat your week are the ones that live in the user's browser and never in your dev build: a hydration mismatch that only triggers because their browser extension rewrote the HTML, a component that renders blank because one fetch in the waterfall 404'd, a layout that breaks only at a 820px viewport you never test. The DOM you can replay is the DOM you can fix.
This is the 2026 playbook for reproducing a rendered-UI bug from the browser's actual state, not the state you assume it was in. It covers DOM-state session replay of the rebuilt tree (so you can open the element panel on a paused frame instead of squinting at a JPEG), the seven independently-sufficient causes of a React hydration error, reading the network waterfall for the dependency ordering that starved a render, the viewport / cache / service-worker / extension state that makes a bug single-user, why INP turning into a Core Web Vital makes a janky interaction a rankings-affecting bug, and how an AI agent can read the captured DOM and console over MCP and point at the offending component before you finish reading the report.
Common pitfalls
The recurring mistakes that get bug reports bounced back — and how to avoid them.
Real-world examples
What these bugs look like in practice, and how to file them cleanly.
Hydration mismatch you cannot reproduce on your own machine
What it looks like: Console reads "Text content does not match server-rendered HTML" and the UI flickers or shifts on first paint — but only for some users, never in your local dev run. The trigger is often an installed extension or CDN minification that exists only in their environment.
How to file it: Capture the user's actual rebuilt DOM, not just the console line, and diff it against the server tree. The replayed frame is inspectable HTML, so you open the element panel on the exact divergent node. Then map it to one of the seven enumerated causes and fix at the source (defer the browser-only value, fix the illegal nesting, or add suppressHydrationWarning on that one node).
// A common cause: a non-deterministic value rendered on the server
// renders a different string on the client during hydration.
// FIX: defer it until after hydration so server and client agree.
const [now, setNow] = useState<string | null>(null);
useEffect(() => setNow(new Date().toLocaleTimeString()), []);
return <span suppressHydrationWarning>{now ?? ''}</span>;Component renders blank because one request in the waterfall failed
What it looks like: A panel mounts empty with no visible error. You cannot tell from the screenshot whether the fetch failed or the component threw, and it works fine against your local API.
How to file it: Read the captured network waterfall to find the request that returned the wrong status or stalled, then correlate its timing to the empty node in the DOM replay. Render an explicit error/empty state instead of silently returning null, and handle the non-200 path so the next failure surfaces itself.
// Blank-on-failure hides the bug. Make the failed request visible.
const { data, error, isLoading } = useQuery(['orders'], fetchOrders);
if (isLoading) return <Skeleton />;
if (error) return <ErrorState detail={String(error)} />; // not `return null`
if (!data?.length) return <Empty />;
return <OrderList items={data} />;Layout breaks only at a viewport size you never test
What it looks like: Renders fine at 375px and 1440px, but stacks or overflows at 768-880px tablet widths. The bug report has a screenshot at the user's odd window size with no dimensions noted.
How to file it: Reproduce at the exact captured viewport — BugMojo records window.innerWidth/innerHeight automatically, so you resize to the failing width instead of guessing. The DOM replay lets you read computed styles at that breakpoint to find the rule that wraps. Then add the intermediate breakpoint your CSS skipped.
/* The 375 and 1440 cases were covered; the 768-880 gap was not. */
@media (min-width: 768px) and (max-width: 880px) {
.toolbar { flex-wrap: wrap; gap: 0.5rem; }
}Workflow comparison
The same bug, filed two ways — with and without a capture tool.
| Feature | BugMojo | Screenshot / Jira attachment |
|---|---|---|
| Inspect the failure as a live DOM tree (element panel, computed styles) | Yes — rrweb DOM replay, pause and scrub any frame | No — flat pixels in an image |
| See the network waterfall (timing + dependency order) behind the render | Captured with the report | Not captured; reporter would re-export a HAR |
| Retain Cookie / Authorization for an auth-dependent 401/403 in the waterfall | Retained for replay | Sanitized HAR strips them |
| Auto-record viewport, console, cache/extension state at the failure instant | Auto-captured | Manual — usually omitted |
| AI agent reads the DOM + console + network over MCP (Claude Code, Cursor) | Yes — agent points at the broken component, drafts a fix or failing test | No — image/attachment a human must interpret |
| Mature production error-monitoring (release health, source-mapped stack aggregation, alerting) | Not its job — pair with Sentry | Sentry / LogRocket own this |
| Continuous always-on session recording of every prod user | No — manual one-click capture at the moment | LogRocket / FullStory record continuously |
| rrweb DOM session replay | Scrubbable, on-demand | Varies / always-on only |
| Zero-setup Quick Capture | No project, no SDK | Account / SDK required |
BugMojo records the DOM, console, and network — then ships a one-click ticket with the full replay attached. No SDK, no setup.
Try BugMojo freeFrequently asked questions
Frequently asked questions
Sources
- Text content does not match server-rendered HTML — enumerated causes of React hydration errors — Next.js (Vercel) (2025)
- Interaction to Next Paint is officially a Core Web Vital — INP replaces FID, 200ms good threshold — web.dev (Google Chrome team) (2024-03-12)
- rrweb-io/rrweb — record-and-replay-the-web: full DOM snapshot + timestamped incremental mutations — rrweb (GitHub) (2025)
- Network features reference — Export HAR (sanitized) strips Cookie / Set-Cookie / Authorization by default — Chrome for Developers (2024-07-16)
- State of JavaScript 2024: Front-end Frameworks — pain-point freeform counts (complexity, performance) — Devographics / State of JS (2024-12)
- Specification 2025-11-25 — Model Context Protocol exposes Resources/Tools to AI hosts over JSON-RPC — Model Context Protocol (2025-11-25)

