frontend-debugging
Skill0 starsChanged 3 months agoApache-2.0View on GitHub
Browser and client-side debugging technique — how to get real evidence out of a running UI instead of guessing from source. Use this skill whenever diagnosing something in the browser or a client app: a component rendering the wrong value, a blank or white screen, a layout or CSS problem, an element that won't click, an infinite re-render or effect loop, a hydration mismatch, stale data after a mutation, a request that fails only from the browser, a minified production stack trace, or a janky/slow interface. Trigger on 'React', 'Vue', 'Svelte', 'Next.js', 'hydration', 're-render', 'useEffect', 'stale closure', 'CSS', 'z-index', 'overflow', 'DevTools', 'console error', 'CORS', 'source map', 'white screen', 'not updating', 'flicker', 'layout shift'. Covers the tooling, the high-frequency bug classes, and how to reproduce a UI bug deterministically.
- Reads credentials
1 other project uses a version of this.
Adopted by
Most recently taken up by
- Arcadi4/skillstheir own version, first seen Jul 6, 2026
Other versions1 project uses a version of this.
Word for word · 0
None found.
Edited · 0
None found.
Wrote their own version · 1
- Arcadi4/skills★ 4same name
How matches are found. Word for word: the exact same text. Edited: shares passages with this file (“99% same” means 99% of its five-word phrases also appear here; 10% or more counts). Their own version: similar meaning, or, weakest, the same name. We can’t tell which project had it first. Projects are counted among the 200 most-starred candidates, and mentions are links to the repository we found, up to 50 per site, so both counts are a floor.
In this repository3 other files it gives its agents.
- backend-debuggingSkill
- debuggable-codeSkill
- root-cause-methodSkill
The file
We split this skill into the rules it gives an agent
12 rules. About 0 are common, 8 are distinctive, and 4 are very specific to this project.
≈0
Common · estimate
Many files like this have a rule of this kind.
≈8
Distinctive · estimate
This author's own choices. Worth a look.
4
Very specific
Tied to their project's names, files or tools. Probably skip.
- Common
- Distinctive
- Very specific
- Distinctive: line 27
The console — all of it: Read every error and warning, not just the red one at the bottom.
- Distinctive: line 31
The Network tab: Before blaming rendering, confirm what actually arrived: status, the response *body*, the request headers you thought you …
- Very specific: line 44
Targeted logging: when the state is too large or too fast for a breakpoint — console.table for arrays of objects, console.trace() to find …
- Distinctive: line 71
When driving a browser programmatically (Playwright, Puppeteer), capture console messages, page errors, and failed requests as part of the …
- Distinctive: line 115
Do this first when behavior contradicts the source:
- Distinctive: line 121
Check the source map is loading, so the stack frames you're reading are real.
- Very specific: line 122
Check which environment the built client is pointed at (NEXT_PUBLIC_* / VITE_* values are baked in at build time — changing the .env …
- Distinctive: line 173
If the console has no JS error at all, suspect the script never ran: a Content-Security-Policy violation blocks the bundle and reports …
- Very specific: line 179
Use the computed styles, not the stylesheet.
- Very specific: line 199
Check the listener is attached to the element you think (inspect → Event Listeners), that nothing calls stopPropagation upstream, that the …
- Distinctive: line 221
Match the reported release: Check out the version the report came from before reproducing.
- Distinctive: line 242
Never optimize from a hunch.
≈ “Common” is an estimate: at least 1 in 5 of the 350,495 skill files we read have a rule of the same kind, not the same words. “Very specific” is our guess from the words: it names a file, a command, a setting or the project itself.
Files
Frontend Debugging
The frontend's defining difficulty is that the failure is far from the cause: a wrong pixel is the end of a chain that started in a server response, passed through a cache, a store, a memo, and a stylesheet written by someone else. Debugging it well means checking the value at each boundary rather than staring at the component that displayed it.
Get evidence from the running page, not from the source
Reading JSX to guess why a value is wrong is the slowest available method. In order of value:
- The console — all of it. Read every error and warning, not just the red one at the bottom. React's warnings ("cannot update a component while rendering", "each child needs a key", hydration mismatch text) usually name the bug outright. Turn on "Preserve log" so a navigation or reload doesn't erase the evidence.
- The Network tab. Before blaming rendering, confirm what actually arrived: status, the response body, the request headers you thought you sent, and whether it was served from cache or a service worker. An enormous share of "the UI is wrong" is "the API returned something different than you think."
- React/Vue DevTools. Inspect the live props and state of the component that's wrong, and of its parent. Use the profiler's "why did this render" to answer re-render questions with data instead of theory.
- The Elements panel. For anything visual, inspect the real computed styles and box model rather than reading the stylesheet. Computed shows you which rule actually won.
- A breakpoint, not a log. Conditional breakpoints (
orderId === "8812"), logpoints, "break on DOM subtree modification" for mystery mutations, and "Pause on caught exceptions" for errors being swallowed. The call stack at the moment of the bug is worth twentyconsole.logs. - Targeted logging when the state is too large or too fast for a breakpoint —
console.tablefor arrays of objects,console.trace()to find who called a function,console.count()for render counts,%cstyling to make a needle findable in a haystack.
Four DevTools features that repay learning them, because each removes an entire category of wasted stepping:
- Ignore-list the framework (Settings → Ignore List, or right-click a frame → "Add script to
ignore list"). Without it, every step-into lands in React's scheduler and every stack is
twenty frames of
node_modules. With it, the stack shows only your code, and "step into" goes where you meant. - Event Listener Breakpoints (Sources → Event Listener Breakpoints → tick
click) answers "which handler actually runs?" without searching for the handler. Essential when the listener is attached by a library or several layers of wrapper. - Async stack traces. The horizontal
Asyncseparator in the call stack shows what scheduled the work, not just what ran it. A promise rejecting three frames away from the code that started it is normal; the separator is how you get back to the origin. - Local Overrides (Sources → Overrides) lets you edit the deployed bundle or a fetched response locally and reload against the real production app. It is the fastest way to test "would this fix it?" before writing a patch, and to confirm a bad API response is the cause by substituting a good one.
Firefox is worth keeping open for two panels Chrome doesn't match: the Accessibility Inspector (the accessibility tree — what a screen reader and most test selectors actually see, which is the answer to "the element is there but the test can't find it") and its Grid inspector.
When driving a browser programmatically (Playwright, Puppeteer), capture console messages, page errors, and failed requests as part of the run — otherwise the most informative output is discarded silently. Take a screenshot at the moment of failure, not after.
When no browser is available at all — headless environment, the automation tool is busy, the user can't run it for you — you are not blocked. Copy the suspect client function verbatim into a scratch script, stub only the browser APIs it touches, and run it against the live server, replaying the user's sequence of actions. This works for any bug in logic rather than rendering: cache keys, state transitions, request construction, data transforms. It is a real reproduction, not a simulation, as long as you copy the code rather than paraphrase it — say in the report that you ran it outside the browser, and what that leaves unverified (anything involving layout, styling, or browser-enforced request policy).
When the bug is a production report you can't reproduce
Most frontend bugs arrive as "a customer saw this once," on a browser, screen size, account, and data shape you don't have. The instinct is to guess from the source. The evidence usually already exists — someone's monitoring recorded it.
- Read the breadcrumb trail before the stack. Error monitors (Sentry, LogRocket, Bugsnag, Rollbar) record the console output, navigations, clicks, and network requests in the seconds before the throw. That trail is the click path you were about to ask the reporter for — and it's what actually happened rather than what they remember doing.
- Watch the session replay if there is one. Thirty seconds of video routinely answers a question that a day of reading would not, because it shows the one interaction nobody thought to mention.
- Slice the issue before reading code. Group the occurrences by release, browser/OS, locale, device, and account. "Only Safari," "only since 4.2.0," "only this one tenant," "only on mobile" each localize the bug before you open a file — and each points at a different investigation.
- Fix the source maps before you read the trace. A minified stack is not evidence. An
unreadable production trace almost always means the map never uploaded, or the release name
the SDK reports doesn't match the one the maps were uploaded under. Repair that first rather
than trying to interpret
a.b is not a function. - Check what the user's environment adds. Extensions mutating the DOM, an ad blocker cancelling a request, a corporate proxy rewriting responses, and an outdated cached bundle are all invisible from your machine and common in the field.
Telemetry gives you the mechanism far more often than the reproduction. Say which one you have: a breadcrumb trail that explains the failure is strong evidence, but until you can make the bug happen on demand the finding is Likely, not Confirmed.
Verify you are debugging the code you think you are
Do this first when behavior contradicts the source:
- Hard reload with cache disabled; check for a service worker holding an old bundle (Application → Service Workers → Unregister).
- Confirm the dev server actually rebuilt — HMR silently fails more often than people believe. A deliberate syntax error should break the page; if it doesn't, you're on a stale bundle.
- Check the source map is loading, so the stack frames you're reading are real.
- Check which environment the built client is pointed at (
NEXT_PUBLIC_*/VITE_*values are baked in at build time — changing the.envwithout rebuilding changes nothing).
The high-frequency bug classes
Wrong or stale value on screen
Walk the value backwards through each boundary and find where it first goes wrong: network response body → parse/transform → cache or store → selector/derived state → props → render. Check it at each one. The boundary where it first diverges owns the bug — everything downstream is a symptom.
Specific causes worth checking early: a query cache key that doesn't include a parameter that
changed (so a different resource returns the previous one's data), a mutation that doesn't
invalidate, an optimistic update that never reconciled, two sources of truth for the same data,
and a useState initialized from a prop that then ignores prop changes.
Nothing updates when state changes
Almost always identity: the state was mutated in place, so the reference never changed and the
comparison saw no update. array.push, obj.field = x, array.sort() on state, or a reducer
returning the same object. Check with a strict-equality comparison before and after.
Also: state on the wrong component, a key that stays the same across a list reorder (or a
key={index} that stays the same while the data moves), a memo whose comparator is too
aggressive, or a subscription that was never established.
Infinite render / effect loop
An effect that sets state which changes a dependency which reruns the effect. The usual culprit
is a dependency that is a new object, array, or function on every render — an inline {},
[], or arrow function in the deps array. Log the dep identity across renders to confirm.
Fix by stabilizing the dependency (useMemo/useCallback, move it out of the component, or
select a primitive), not by removing it from the array and pretending the dependency isn't real.
Stale closure
A callback captured an old value — common in setInterval, event listeners registered once,
debounced handlers, and effects with a truncated dep array. Symptom: the value in the callback
is always the initial one, or one render behind. Confirm by logging inside the callback and
comparing to a render-time log.
Hydration mismatch (SSR)
Something differed between server and client render: Date.now(), Math.random(), localStorage,
window, locale/timezone formatting, a browser extension mutating the DOM, or invalid HTML
nesting that the parser silently corrects (<div> inside <p>, block inside <button>). Read
React's mismatch message — the newer ones print the offending element. Reproduce with JS
disabled to see exactly what the server sent.
White screen / blank page
An error thrown during render with no error boundary above it. Get the real error: check the
console first, and if the trace is minified, run a development build or load the source maps.
Also check for a failed dynamic import (a stale chunk after a deploy), a router with no matching
route, and a top-level await or module-scope throw that kills the bundle before mount.
If the console has no JS error at all, suspect the script never ran: a Content-Security-Policy
violation blocks the bundle and reports itself only as a CSP violation message (not an
exception), so the page looks like it simply didn't load. The same applies to a blocked inline
style or an API call refused by connect-src — the report names the directive that rejected it.
Visual and layout bugs
Use the computed styles, not the stylesheet. The recurring causes:
overflow: hidden on an ancestor clipping a dropdown; z-index doing nothing because the
element isn't positioned, or being trapped in an ancestor's stacking context created by
transform, filter, opacity < 1, will-change, or backdrop-filter; a flex or grid child
refusing to shrink because min-width/min-height defaults to auto; collapsing margins;
100vh on mobile; a specificity or cascade-layer conflict (Elements → Styles shows the
strikethrough of the rule that lost).
For "I can't click it": something invisible is on top. Inspect at the click point, check for a
full-viewport overlay, pointer-events, and elements with negative margins. For "it moves after
load": check for late-loading fonts, images without dimensions, or a scrollbar appearing.
Requests that fail only from the browser
CORS failures are configured on the server and reported on the client — the browser message
names the missing header. Preflight (OPTIONS) is a separate request that must succeed on its
own; check that it's answered and not swallowed by auth middleware. Also: cookies not sent
because credentials wasn't set or SameSite blocks it, mixed content, and a request that
looks fine in curl because curl doesn't enforce any of this.
Events not firing
Check the listener is attached to the element you think (inspect → Event Listeners), that
nothing calls stopPropagation upstream, that the handler isn't being re-created and removed
each render, that preventDefault isn't suppressing the default you wanted, and that a
pointer-events/disabled attribute isn't blocking it. Passive listeners cannot
preventDefault scroll.
Reproducing a UI bug deterministically
- Pin the inputs: the exact route, the exact record, the exact viewport, the exact auth state. Half of "sometimes" is "with this one record."
- Test in a private window to eliminate extensions, and with a cleared
localStorage/ IndexedDB to eliminate stale client state — then deliberately restore the stale state, since that's often the actual bug. - Throttle the network and CPU in DevTools to make timing and race bugs reproducible. Many "only in production" bugs are simply "only when the response is slow."
- Check the other browser. Safari/Firefox differences are real (date parsing,
flexdetails, private-mode storage, autoplay policies) and identifying it as one-browser-only is half the fix. - Use a real device for a mobile bug. DevTools' device emulation changes the viewport and the
user agent; it does not give you iOS Safari, its scroll and viewport behavior, its storage
rules, or its touch handling. Attach the real thing — an iPhone over USB through Safari's Web
Inspector, an Android device through
chrome://inspect— and debug the actual engine. A large share of "only on mobile" bugs are "only in WebKit." - Match the reported release. Check out the version the report came from before reproducing.
Trying to reproduce a two-week-old report against current
mainproduces a false "cannot reproduce" when the bug is real and still deployed. - Once reproduced by hand, write it as a Playwright/Cypress test so the fix is verifiable and the bug can't return.
Performance and jank
Measure before changing anything. Use the Performance profiler and the framework profiler:
- Slow interaction (INP) — long task on the main thread. Find it in the flame chart; it's usually a large re-render, a synchronous layout thrash (read/write/read of layout properties in a loop), or a heavy computation that belongs in a memo or a worker.
- Excessive re-renders — the React profiler shows what rendered and why. Context value recreated on every render is the most common cause of a whole-tree re-render.
- Slow load (LCP) — usually the network waterfall, not the JS: an unoptimized hero image, a render-blocking resource, a font, or a request chain that could be parallel.
- Memory growth — Memory panel, heap snapshot comparison between two points after the same action. Detached DOM nodes point to listeners or timers never cleaned up in an effect's teardown.
Never optimize from a hunch. The profiler routinely disagrees with the obvious suspect.
Does it work?
Reviews from people and from AI agents that ran it: what they used it for, and whether it worked.
Reviews can't be read right now.
Your trail
Files you open will collect here, so you can find them again.
Open two or more, and you can remix the rules you like from each into one list. Kept in this browser only.
Now do this for your own.
Bring your CLAUDE.md, AGENTS.md or skills. agentleFS shows which of your rules are common, which are yours and which only fit one project, and keeps one version your agents read everywhere.

