Pick your stack. Each guide is framework-specific — real gotchas, real bugs, real fixes.
BugMojo works with every modern frontend stack, but the gotchas are wildly different. Here are framework-by-framework guides written by engineers who have actually shipped on each — what tends to break, what to capture, and how to plug BugMojo in.
React is the most-shipped frontend framework in the world, and it has the most distinctive failure modes in the world. The bugs your team will spend the most time on are not "the button doesn't render" — they're hydration mismatches, race conditions inside useEffect, stale closures around setState, suspense boundary thrashing, and concurrent-mode re-entry. Each of these is invisible in a screenshot and obvious in a session replay.
Next.js 15 ships with the App Router, React Server Components, Turbopack, and PPR (Partial Pre-Rendering) as defaults. Each unlocks performance — and each introduces a class of bug your old Pages Router playbook does not cover. Hydration boundaries are now everywhere, edge runtime errors look different from Node errors, and the "use client" / "use server" boundary is a frequent source of mystery payloads.
Vue 3 reactivity is elegant when it works and quietly wrong when it does not. The most common bugs in Vue apps fall into two buckets: reactivity bugs (you mutated a ref directly, you destructured a reactive object, you assigned a new array instead of mutating in place) and lifecycle bugs (you set up a watcher and forgot to stop it, you read `$refs` before the component mounted). Neither shows up in a screenshot.
A WordPress page is rarely one team's code. It runs a theme plus a dozen independently-authored plugins, and as of June 2026 WordPress powers 41.9% of all websites and 59.4% of the known CMS market (W3Techs), so the front end you are debugging is a stack of third-party scripts that have never been tested together. When one of them throws a console error, the visible symptom (a broken slider, a checkout button that does nothing, a form that won't submit) tells you almost nothing about which of those scripts failed. The default and usually correct hypothesis is "it's a plugin" — Patchstack logged 7,966 new ecosystem vulnerabilities in 2024, up 34% year over year, overwhelmingly in third-party plugins rather than core — but knowing that does not tell you which plugin, and that is the expensive part.
Angular 19 (with Signals, standalone components, and the new control-flow syntax `@if`/`@for`/`@switch`) is a different framework than Angular 14 — but most production Angular bugs in 2026 still trace back to one of three sources: change detection running too often, RxJS subscriptions never cleaning up, or NgZone confusion around async work. Each shows up as performance regressions or memory leaks rather than visible UI errors.
Svelte 5 replaced the magic of "assign to a variable, the UI updates" with **runes** — `$state`, `$derived`, `$effect`. The new system is more explicit and faster, but it changes how every Svelte bug manifests. SvelteKit 2 layered on top brings server load functions, form actions, and edge-deployed adapters — each with its own failure modes.
Astro's pitch is "ship zero JavaScript by default" — but the bugs your team will hit are not in the static HTML, they're at the **island boundaries**. A `<MyButton client:visible />` hydrates only when scrolled into view; a `<MyForm client:load />` hydrates on every page. Picking the wrong directive turns a 0-KB page into a 200-KB page, or a fast page into a janky one.
Laravel reports a validation failure two completely different ways depending on how the request arrived. A traditional Blade or Inertia full-page POST gets a 302 redirect back with errors flashed to the session (the $errors MessageBag) plus the old() input; an XHR or JSON request to the same controller, hitting the same FormRequest, gets an HTTP 422 with a structured {message, errors{}} body that uses dot notation for nested fields like "users.0.email". Same code, two browser-visible outcomes, which is exactly why a screenshot is ambiguous and a captured network HAR is decisive.
A Remix bug rarely lives on one side of the wire. The loader runs on the server, the action's revalidation runs back on the client, and the ErrorBoundary renders on whichever side actually threw — so the failing data shape and the rendered fallback end up on different halves of the request. That split is why a screenshot is nearly useless here: it shows the client-side fallback while the real cause sits in a server loader you cannot see from the DOM.
Django answers an unsafe request with HTTP 403 Forbidden the moment CsrfViewMiddleware rejects it, and on an htmx app that 403 is easy to misread. A server-rendered form carries the token through the {% csrf_token %} tag, which injects a per-response masked value that also defends against the BREACH attack; an AJAX or htmx POST to the same view instead has to send the token in the X-CSRFToken header (configurable via CSRF_HEADER_NAME). The django-htmx docs recommend setting it once so every element inherits it: <body hx-headers='{"x-csrftoken": "{{ csrf_token }}"}'> — and note that is {{ csrf_token }} the variable, not the {% csrf_token %} hidden-input tag. For HTTPS requests with no Origin header Django adds strict Referer checking on top, so a proxy that rewrites those headers can produce a 403 that looks like nothing the user did. The failing request's headers and cookies settle it instantly; a screenshot cannot.
Rails shipped its frontend as React for years, but that era is over. In the 2024 Ruby on Rails Community Survey of 2,709 developers, Stimulus (31%) overtook React (24%) as the most-used JavaScript library alongside Rails — which means the typical 2026 Rails frontend bug is a Hotwire bug, not a React bug. The failures your team now spends the most time on are a Turbo Frame that renders "Content missing", a Stimulus data-action that silently never fires, a Turbo Stream broadcast that updates the wrong element, and a Turbo 8 morph that resets scroll or JavaScript state. None of these throw a stack trace you can paste into an issue; all of them are decided by the exact DOM, console, and network state at the failure instant.
Nuxt is the default meta-framework for Vue teams, and that universal SSR posture is exactly what makes its bug profile distinct from plain client-only Vue. The errors you will lose the most time to live on two boundaries: the hydration seam where server-rendered HTML meets client Vue, and the Nitro server layer where your /server/api routes run. Nuxt's own best-practices doc enumerates five mismatch causes by name — browser-only APIs like localStorage and window.innerWidth, non-deterministic values like Math.random(), time-dependent content like new Date(), invalid HTML nesting the browser silently repairs, and third-party libraries with DOM side effects — and prescribes ClientOnly, useCookie, CSS media queries, onMounted deferral, and the NuxtTime component as fixes.
React Native for Web is documented as "a compatibility layer between React DOM and React Native" that "uses React DOM to accurately render React Native compatible JavaScript code in a web browser." That one sentence is why a Chrome extension is relevant to a React Native team at all: the Expo Web or react-native-web build is real browser HTML, not a native view tree, so an rrweb DOM replay, console capture, and a network HAR all work on it exactly as they would on any React website. This page is about the web target of a universal React Native project — and only that target.
Flutter Web is a different beast for bug capture than React or Vue. Under the default CanvasKit renderer your entire app paints to a single HTML canvas element, so a DOM session replay records one opaque node instead of the widget tree, and the visual detail a tool like rrweb relies on is simply not in the DOM.