agentleFS
kenjichristopherv-delkenjichristopherv-del/debugmaxxing/skills/frontend-debugging/SKILL.md

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

  • Other versions1 project uses a version of this.

    Word for word · 0

    None found.

    Edited · 0

    None found.

    Wrote their own version · 1

    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.

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
  1. Distinctive:

    The console — all of it: Read every error and warning, not just the red one at the bottom.

    line 27
  2. Distinctive:

    The Network tab: Before blaming rendering, confirm what actually arrived: status, the response *body*, the request headers you thought you …

    line 31
  3. Very specific:

    Targeted logging: when the state is too large or too fast for a breakpoint — console.table for arrays of objects, console.trace() to find …

    line 44
  4. Distinctive:

    When driving a browser programmatically (Playwright, Puppeteer), capture console messages, page errors, and failed requests as part of the …

    line 71
  5. Distinctive:

    Do this first when behavior contradicts the source:

    line 115
  6. Distinctive:

    Check the source map is loading, so the stack frames you're reading are real.

    line 121
  7. Very specific:

    Check which environment the built client is pointed at (NEXT_PUBLIC_* / VITE_* values are baked in at build time — changing the .env …

    line 122
  8. Distinctive:

    If the console has no JS error at all, suspect the script never ran: a Content-Security-Policy violation blocks the bundle and reports …

    line 173
  9. Very specific:

    Use the computed styles, not the stylesheet.

    line 179
  10. Very specific:

    Check the listener is attached to the element you think (inspect → Event Listeners), that nothing calls stopPropagation upstream, that the …

    line 199
  11. Distinctive:

    Match the reported release: Check out the version the report came from before reproducing.

    line 221
  12. Distinctive:

    Never optimize from a hunch.

    line 242

≈ “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.

Keep what fits and drop the rest. Your version, ready for your agents.
Files
SKILL.md

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:

  1. 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.
  2. 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."
  3. 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.
  4. 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.
  5. 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 twenty console.logs.
  6. Targeted logging when the state is too large or too fast for a breakpoint — console.table for arrays of objects, console.trace() to find who called a function, console.count() for render counts, %c styling 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 Async separator 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 .env without 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, flex details, 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 main produces 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.

Download now (ZIP)

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.