afterbuild/ops
§ EX-02/react-debugging-expert
React debugging for AI-built apps

React debugging for AI-built apps — stop the white-screen crash with React error boundary, hydration mismatch fix and useEffect loop fix

By Hyder Shah12 min readLast updated 2026-04-15
Quick verdict

React debugging for AI-built apps is a distinct specialty: AI builders ship without a React error boundary, run into hydration mismatch bugs the moment Date.now() hits SSR, and wire useEffect infinite loops because dep arrays contain inline objects. We trace the crash, ship the hydration mismatch fix and useEffect loop fix, and install route-level error boundaries so one broken view can never take down a Lovable, v0, Bolt or Cursor app again.

Why AI builders ship broken React debugging

React is forgiving of good patterns and brutal to bad ones, and AI builders default to bad ones. The model writes a Server Component that imports a client hook, a useEffect that re-runs forever because the dependency array has an inline object, a list with no keys that shuffles every render, a Suspense boundary with no fallback — and the preview happens to survive because nothing errored on that specific data shape. Then a real user hits a null and the whole app dies.

The core failure mode, quoted repeatedly by users: 'one component throwing an error can cause the whole app to fail.' That is React's default behaviour. Lovable, Bolt.new, v0 and Cursor almost never ship error boundaries. They rarely ship Suspense fallbacks. They frequently mix 'use client' and server-only imports in ways that break hydration. The preview lies; production crashes.

Source: Medium / Nadia Okafor — Vibe Coding in 2026

§ MATRIX/ai-builder / failure-mode

Which AI builder shipped your broken React debugging?

The React debugging failure mode usually depends on the tool that shipped the code. Find your builder below, then read the matching problem page.

AI builderWhat breaks in React debuggingGo to
LovableNo error boundaries, useEffect infinite loops, state at wrong levelLovable rescue
Bolt.newHydration mismatches between preview and deploy, stale closuresBolt rescue
v0Mixes server/client components incorrectly, breaks on navigationv0 broken app
CursorBy file seven forgets component contracts, passes wrong prop shapesCursor regression loop
Replit AgentUnkeyed lists, race conditions on async stateReplit rescue
Claude CodeRefactors without catching downstream render assumptionsClaude Code refactor
WindsurfLarge component trees with prop drilling bugs and missing memoizationWindsurf rescue
Base44Proprietary component layer hides the actual React errorsBase44 broken
§ ANATOMY/react debugging / failure

Anatomy of a React crash in an AI-built app

A healthtech founder opened a ticket at 2am: 'my app is broken, white screen, everyone is logged out.' The app was a Cursor-built patient intake tool on Next.js App Router, shipping for 90 days, 600 active clinicians. The stack trace, when we pulled it from Sentry, told the whole story in three lines: 'TypeError: Cannot read properties of null (reading map)' thrown in a patient list component, unhandled, bubbling up to the Next.js root layout.

The path that led here is the exact regression pattern quoted in the user-needs vocabulary — 'the filter worked, but the table stopped loading. I asked it to fix the table, and the filter disappeared.' Two weeks earlier, the founder had asked Cursor to add an optional field to the patient schema. Cursor happily did, across four files. It also silently broke the assumption in the list component that the `notes` array was always present. A new patient was created without notes on Monday morning. The component rendered, tried to call `.map` on undefined, threw. No error boundary in the tree. Next.js's default 'something went wrong' page replaced the app. Every clinician logged in saw the same dead page.

The engagement cost $499 on the emergency triage tier.
Hyder Shah· React debugging rescue engineer

The fix took an afternoon. We added a route-level error boundary using Next.js App Router's error.tsx convention, defended the component against nullish notes with optional chaining, and shipped a Playwright test asserting the page renders when notes is undefined. We also added Sentry and a feature-level error boundary around the table, so the next time this pattern happens — and with AI-generated code it will happen again — the clinician sees 'this panel failed to load, try again' instead of a blank app. The engagement cost $499 on the emergency triage tier. The outage cost the founder a Monday morning and a support ticket from their biggest customer.

The structural lesson repeated across every React rescue: AI builders ship the happy path. React's default behavior when any render throws is to unmount the entire tree unless a boundary catches it — and AI builders almost never add the boundaries. Add to that the patterns Medium's Nadia Okafor documented in Vibe Coding in 2026 — hooks called conditionally, effects that depend on inline objects and loop forever, lists keyed by array index that lose focus on reorder — and you have a component tree that holds together only as long as nobody creates a new kind of data. A real user always does. The rescue is not to rewrite; it is to add the three or four structural pieces (boundaries, typed fetches, query layer, regression tests) that make the tree resilient to the next prompt.

§ RESCUE/react debugging / engagement

What a React debugging rescue engagement ships

From first diagnostic to production handoff — the explicit steps on every React debugging engagement.

  1. 01

    Free rescue diagnostic

    Send a repro and the repo. We reproduce the crash, capture the exact stack trace and component boundary it happens at, and send a written diagnosis within 48 hours.

  2. 02

    Scope and fixed price

    You get a written fix plan: which components need error boundaries, which hooks need to move, which effects need cleanup. Fixed price, no hourly meter.

  3. 03

    Fix the crash, add boundaries

    We fix the specific bug, then add React error boundaries at the route and feature levels so a single future crash can never take down the whole app again.

  4. 04

    Kill the useEffect loops

    Audit every useEffect, fix dependency arrays, move derived state out of effects, and replace imperative fetches with TanStack Query or equivalent.

  5. 05

    Regression test and handoff

    Ship Playwright tests that fail when the old bug recurs. You get the tests, the patches, and a runbook.

§ AUDIT/react debugging / first-pass

Every React debugging rescue audit checks

The diagnostic pass on every React debugging rescue. Each item takes under 10 minutes; together they cover the patterns that cause 90% of AI-built-app failures.

  1. 01
    Error boundaries at the route level

    Next.js App Router's error.tsx and global-error.tsx files. If they're missing, one rendering error anywhere blanks the entire page.

  2. 02
    Error boundaries at the feature level

    Dashboards composed of panels should wrap each panel so one bad API response degrades one tile, not the whole view.

  3. 03
    Hydration mismatches in server components

    We grep for Date.now(), Math.random(), new Date(), and window references inside render paths. These cause hydration warnings that escalate to blank pages on some browsers.

  4. 04
    useEffect dependency arrays

    Inline objects, arrays, or functions in a dep array cause infinite re-renders. We scan every useEffect and flag the ones with non-primitive deps.

  5. 05
    Keys on list renders

    Missing keys cause re-mounts on every render — losing input focus, scroll position, form state. Keys based on array index cause re-mounts on insert/delete. Both get flagged.

  6. 06
    'use client' boundaries

    Server components that import client-only hooks crash at build. Client components that try to import server-only modules crash at runtime. We validate every boundary.

  7. 07
    Suspense fallbacks

    Every async boundary must have a fallback. Missing fallbacks show blank space and can break parent layouts.

  8. 08
    State placement

    We check for state lifted too high (causing unnecessary re-renders) or kept too local (causing prop drilling). Zustand/Context usage reviewed.

  9. 09
    Memoization hotspots

    We profile with React DevTools to find components rendering more than their parent. useMemo and memo() applied surgically where needed.

  10. 10
    Fetch error handling in the rendering layer

    Every fetch should have an error state in the UI. We find the fetches that crash the component when the API returns 500.

§ DIFF/react debugging / before-after

Common React debugging patterns we fix

These are the shapes AI-generated code arrives in — and the shape we leave behind.

The misplaced 'use client'
✕ before · ai-shipped
tsx
01A server component imports a Zustand store, which uses React hooks internally. Build fails with 'cannot use hooks in server component', the AI adds 'use client' to the root layout, and now the entire app re-renders on the client with no server benefits.
The misplaced 'use client'
✓ after · afterbuild
tsx
01Client and server components separated cleanly. Interactive islands wrapped in 'use client'; data fetching stays on the server via async server components. The boundary is intentional, not reactive.
The misplaced 'use client'
Unkeyed list items
✕ before · ai-shipped
tsx
01Items map with the array index as the key. Reordering, deleting, or inserting causes the wrong item to get focus or lose its form state.
Unkeyed list items
✓ after · afterbuild
tsx
01Stable unique IDs as keys. React's reconciler tracks identity through reorders. Focus and form state persist correctly.
Unkeyed list items
Infinite useEffect
✕ before · ai-shipped
tsx
01useEffect runs a fetch, sets state, state change triggers re-render, re-render re-runs the effect because the dep array contains a new object literal. Browser tab heats up, network panel floods.
Infinite useEffect
✓ after · afterbuild
tsx
01Memoize the dep or move it outside render. Prefer a data-fetching library (TanStack Query) that dedupes requests automatically.
Infinite useEffect
Hydration-breaking Date
✕ before · ai-shipped
tsx
01Page renders `{new Date().toLocaleString()}` in a server component. Server time and client time differ by milliseconds; HTML mismatch; React logs warning; production blanks the page.
Hydration-breaking Date
✓ after · afterbuild
tsx
01Render the raw ISO timestamp server-side, format in a client component after mount — or use a fixed format with a stable timezone.
Hydration-breaking Date
Props contract drift
✕ before · ai-shipped
tsx
01Parent passes `{ user: User | null }`; child assumes `user.name` exists. Crash on the one user who signed up via OAuth without a full profile.
Props contract drift
✓ after · afterbuild
tsx
01Strict types, exhaustive null checks, default rendering when the shape is incomplete.
Props contract drift
The God component
✕ before · ai-shipped
tsx
01A 1,200-line page.tsx with 40 useState calls, 15 useEffects, inline handlers for everything. Any change re-renders all of it.
The God component
✓ after · afterbuild
tsx
01Extract independent concerns into their own components, each with its own state. Shared state in context or a store. Each piece rerenders only when its data changes.
The God component
Silent failed fetches
✕ before · ai-shipped
tsx
01fetch('/api/data').then(r => r.json()).then(setData). No catch, no status check. On 500, `setData(undefined)` — downstream component crashes.
Silent failed fetches
✓ after · afterbuild
tsx
01Typed client that throws on non-2xx, error state in React Query, UI renders 'failed to load, retry' instead of crashing.
Silent failed fetches
Everything in a useEffect
✕ before · ai-shipped
tsx
01Derived state computed in useEffect. Data transformation computed in useEffect. 'The page flickers when I type' — because every keystroke triggers an extra render pass.
Everything in a useEffect
✓ after · afterbuild
tsx
01Derive in render. Compute in useMemo if expensive. useEffect reserved for synchronization with external systems (DOM, analytics, subscriptions).
Everything in a useEffect
§ FLAGS/react debugging / red-signals

React debugging red flags in AI-built code

If any of these are true in your repo, the rescue is probably worth more than the rewrite.

flag
signal
why it matters
No error boundaries anywhere in the tree
Every uncaught render error blanks the whole app. Classic AI-builder default.
useEffect with no dependency array or with an inline object/array/function
Either runs every render or infinite loops. Both produce the same user experience: the app feels broken.
'key={index}' on a list of editable items
Deleting a middle item causes the next item to visually 'become' the deleted one — inputs lose focus, form state moves to the wrong row.
Date.now() or Math.random() in a server component render path
Guaranteed hydration warning on every render. Escalates to blank pages on Safari and older Chromium.
'Rendered fewer hooks than during the previous render' in console
Conditional hook calls. Usually an early return inside a component that calls hooks below the return. Guaranteed runtime crash on state transitions.
Same useState value lifted to three different ancestors
AI lifted state 'to be safe' in multiple refactors. Every setter fires three re-renders across half the tree.
No React.StrictMode in the root
You'll ship effect-doubled bugs and Suspense-misuse bugs to production. StrictMode surfaces them in dev.
§ PRICING/react debugging / fixed-price

Fixed-price React debugging engagements

No hourly meter. Scope agreed up front, written fix plan, delivered on date.

price
Free
turnaround
Free rescue diagnostic
scope
30-min call + written triage within 48 hours.
View scope
featured
price
From $499
turnaround
Emergency triage
scope
24hr response, find the root cause, quote the fix.
View scope
price
$3,999
turnaround
Break the fix loop
scope
Two-week engagement to stop the regression cycle.
View scope
price
$7,499
turnaround
Finish my MVP
scope
Full productionisation including error boundaries + tests.
View scope
§ EXAMPLES/react debugging / real-scopes

What React debugging rescues actually cost

Anonymized, representative scopes from recent React debugging rescues. Every price is the one we actually quoted.

Small rescue
$499

A founder whose v0 landing page started white-screening on mobile Safari after adding a testimonial carousel. Hydration mismatch caused by a Date call in a client-side-only hook.

Scope
Single bug reproduced, root-caused, fixed with a Playwright regression test.
Duration
48 hours
Medium rescue
$3,999

A seed-stage team who has 40 'the AI broke it' bugs per week. They want the regression loop to stop.

Scope
Two-week engagement. Error boundaries at every route, TanStack Query layer, 25 targeted bug fixes, Playwright coverage on the five critical user flows.
Duration
2 weeks
Large rescue
$7,499

A growth-stage SaaS with a 40-component dashboard that rerenders on every keystroke, hangs on slow networks, and crashes under real load.

Scope
Full React architecture pass. Memoization, state layer rewrite, Suspense boundaries, server components where applicable, 60 PRs with green CI.
Duration
5-6 weeks
§ DECISION/react debugging / migrate-or-patch

Patch in place, refactor the architecture, or migrate to Server Components?

When a React app crashes, the first question is whether the fix is a patch or a structural change. About two thirds of the React bugs we rescue are patches — a missing key, an un-cleaned-up effect, a hydration mismatch. We fix them in place, add a regression test, and the app moves on. The rescue is an afternoon to a few days, fixed price, no architectural disruption.

About a quarter of the apps we see need a state and data-fetching refactor. The symptoms are excessive re-renders, props drilled through five layers, useEffect-as-data-fetcher anti-patterns, and a state shape that fights every change. The fix is to introduce TanStack Query for server state, isolate UI state to where it's used, and restructure the components so each renders exclusively on the data it depends on. This is a 2 to 4 week engagement and the visible effect is dramatic — keystrokes that used to lag now feel instant, scroll restoration works, and the team's velocity on new features goes up sharply.

The remaining tenth of apps benefit from a partial migration to Server Components. The signal is that most of the page is data the user does not interact with, and the AI builder shipped it as fully client-rendered with all the hydration and bundle-size cost that implies. Moving the static parts to async server components shrinks the JavaScript payload, removes hydration mismatches by definition, and makes initial loads faster. We do this carefully, one route at a time, behind feature flags, because the failure mode of doing it wrong (a server component importing a client-only module) is loud and obvious.

§ RUNBOOK/react debugging / references

React debugging runbook and reference material

The documentation, CLIs, and specs we rely on for every React debuggingengagement. We cite, we don't improvise.

§ FAQ/react debugging / founders-ask

React debugging questions founders ask

FAQ
Why does one error break my entire React app — and how does a React error boundary fix it?
React unmounts the whole tree when a render throws and there's no error boundary above it. AI builders don't add boundaries by default. A React error boundary at the route and feature level contains a single crash to one card or panel, not the whole page. The boundary itself is about ten lines of code, but where you place it matters enormously — a boundary at the wrong level either catches nothing or catches too much, both of which produce a worse user experience than no boundary at all.
What is a hydration mismatch and how do you ship the hydration mismatch fix?
A hydration mismatch is when the HTML rendered on the server doesn't match what React renders on the client. React warns and can blank the page. Common causes: Date.now(), Math.random(), window references, or localStorage read during render. Our hydration mismatch fix finds them with React DevTools and moves them to useEffect or a client-only component. The frustrating part is that hydration bugs often surface only on certain browsers, certain networks, or certain timezones — preview environments rarely reproduce them, and production sometimes doesn't either until a user with a slightly unusual setup loads the page.
Can React debugging for AI-built apps fix a useEffect infinite loop without a rewrite?
Yes, almost always. The useEffect loop fix is usually 3-10 specific bugs plus missing infrastructure (React error boundary, TanStack Query layer, tests). We fix in place; no rewrite needed. The exception is when the entire component architecture is built around a state-management pattern that fundamentally fights React's rendering model — in that case we usually recommend a focused refactor of the state layer rather than a full rewrite.
Do you handle Next.js 16 React debugging or older React versions?
Both. We work with React 18, 19 and the new Server Components model. Most AI-built apps we debug are on Next.js App Router with some 'use client' confusion — which is our specialty. We also handle older Next.js Pages Router projects, although we usually flag whether a migration to App Router is worth bundling into the rescue.
What's your typical turnaround on a React debugging engagement?
Emergency triage: 24 hours. Scoped React debugging engagement: 3-10 business days depending on scope. Break the fix loop engagements: two weeks. We are honest about the difference between 'I can fix this' and 'I can fix this in a way that won't recur'; the second always takes longer, and we will quote both options if asked.
Will you add Playwright tests for the React crash you fixed?
Yes. Playwright for the failure paths that mattered, Vitest for the component-level regression. Every bug we fix ships with a test that would have caught it. The tests assert behaviour, not implementation, so they survive a future refactor — including a future AI-driven refactor — without false-failing.
Can React debugging for AI-built apps also fix the backend bugs that surface as React errors?
Yes — most 'React crashes' are actually the UI failing to handle a 500 or a null. We fix both sides: proper error handling in the fetch layer, and resilient rendering in the UI wrapped in a React error boundary.
Do you fix mobile-only hydration mismatch bugs?
Yes. iOS Safari and older Android WebViews are the source of roughly a third of the 'works on my machine' hydration bugs we see — viewport issues, touch event handling, scroll-restoration problems, hydration discrepancies caused by Safari's stricter parsing. We test on real devices, not just Chrome DevTools' mobile emulation.
What does 'break the fix loop' mean for React debugging for AI-built apps?
It means installing the things AI builders skip: route-level React error boundary coverage, a TanStack Query layer with consistent error handling, Playwright tests on the four critical user flows, a CLAUDE.md / .cursorrules file that tells the AI to never delete an error boundary, and a CI gate that fails on `: any` or missing keys. After the loop is broken, the AI tool can keep editing the codebase without silently undoing the resilience patterns.
Next step

Your AI builder shipped broken React debugging. We ship the fix.

Send the repo. We'll tell you exactly what's wrong in your React debugging layer — and the fixed price to ship it — in 48 hours.

Book free diagnostic →