JavaScript Error Monitoring: 12 Best Practices to Cut Noise & Ship Fixes Faster
Most of what shows up in a JavaScript error tracker isn't a bug you need to fix; it's noise. Third-party scripts, browser extensions, and edge-case devices flood the feed, and the errors actually hurting your users get lost in it.
This guide covers 12 production-tested practices for setting up JavaScript error monitoring that surfaces real, user-impacting bugs, not console spam, plus the error types you'll run into most often and how to configure alerting so your team stops getting paged for noise.
Table of Contents:
- What is JavaScript error monitoring?
- Common JavaScript errors you'll see in production
- 12 JavaScript error monitoring best practices
- Handling errors in React, Vue, and other SPAs
- Common mistakes that undermine JavaScript error monitoring
- Why teams use Atatus for JavaScript error monitoring
- Frequently asked questions
What is JavaScript error monitoring?
JavaScript error monitoring is the automatic capture of runtime errors, exceptions, and unhandled promise rejections that occur in a user's browser; paired with the stack trace, browser, device, user, and session context needed to reproduce and fix each one. Instead of waiting for a support ticket, your team sees the error the moment it happens, with enough detail to go straight to the broken line of code.
Done well, it's a core part of real user monitoring (RUM) where errors get correlated with the page, the user flow, and the Core Web Vitals around them, so you understand not just that something broke, but how much it hurt the experience.
Common JavaScript errors you'll see in production
Before diving into practices, it helps to know what you're actually going to see once monitoring is live. A handful of error types account for most of the noise in any frontend error feed:
| Error | What it usually means | Typical fix |
|---|---|---|
Script error.
|
Cross-origin script threw, but the browser hid the detail |
Add crossorigin attribute + CORS header
|
TypeError: Cannot read properties of undefined
|
Accessing a property on a value that's null/undefined, often a race condition or missing null check | Guard the access; check what's still loading |
ChunkLoadError
|
A code-split bundle failed to load, usually a stale chunk hash after deploy | Detect version mismatch and prompt reload |
ResizeObserver loop limit exceeded
|
Browser safeguard against a resize feedback loop, usually benign | Debounce the callback; track frequency, don't page on it |
| Unhandled promise rejection |
A rejected promise with no .catch(), causing silent failures in async code
|
Add unhandledrejection listener + explicit .catch()
|
| Network / CORS errors on fetch or XHR | API call blocked or failed, often an environment or auth issue | Correlate with backend errors; check CORS config |
Out of memory
|
Memory leak, usually from unbounded arrays, listeners, or timers never cleaned up | Profile with heap snapshots; clean up on unmount |
The two biggest traps here: teams either drown in the top three (script errors, ResizeObserver, and third-party noise) and stop looking at their error feed altogether, or they filter too aggressively and silence a real regression along with the noise. The practices below are built to avoid both.
See your real JS errors, not the noise
Atatus filters bots, domains, and third-party scripts automatically so your team only sees errors from your own code.
12 JavaScript error monitoring best practices
You can start monitoring errors in your JavaScript applications with the following best practices in mind. The first six deal with cutting noise at the source; the rest are about getting enough context to fix what's left, fast.
#1 Whitelist your domains
Domain whitelisting is the single most effective way to eliminate noisy JavaScript errors you don't own. If you load debugging scripts or third-party widgets from another domain, or someone copy-pastes your JS onto another site (more common than you'd think for large, public-facing sites), whitelisting keeps those errors out of your feed entirely even if they're using your API key.
With Atatus, whitelist your domains like this:
atatus.setAllowedDomains(['acme.com', 'www.acme.com', 'api.acme.com']);
#2 Fix "Script error." with CORS + crossorigin
If your page loads scripts from other domains, you'll see a flood of unhelpful Script error. messages. This happens because the browser's same-origin policy blocks error detail from leaking across domains. To get real stack traces from cross-origin scripts, you need two things:
Cross-origin attribute - add crossorigin to the script tag:
atatus.setAllowedDomains(['acme.com', 'www.acme.com', 'api.acme.com']);CORS HTTP header - the server or CDN hosting the script must serve it with:
Access-Control-Allow-Origin: *
Skip either one and you'll keep seeing "Script error." with no file name, no line number, and no way to act on it.
#3 Use source maps and keep them private
Production JavaScript is minified and bundled, so a raw stack trace points at unreadable, transformed code. Source maps translate that back to your original file names and line numbers, so your error tool can show you the actual line that crashed instead of a wall of minified gibberish.
If your source map is publicly accessible, Atatus downloads it automatically; otherwise, upload it via the API.
#4 Catch unhandled promise rejections explicitly
A monitoring setup that only listens for the error event misses an entire category of failures: rejected promises with no .catch(). These fail silently in the browser console and can break async flows - a failed fetch, a broken await chain without ever showing up as a "real" error.
window.addEventListener('unhandledrejection', (event) => {
// event.reason holds the rejection value
atatus.notify(event.reason);
});Pair this with explicit .catch() blocks or try/catch around await in your own async functions, rather than relying on global capture alone.
#5 Tame ResizeObserver loop noise
ResizeObserver loop limit exceeded floods error trackers industry-wide and is almost never the crash it sounds like, it's a browser safeguard that fires when a resize callback triggers another resize. Debounce your ResizeObserver callbacks to reduce it at the source, and track frequency rather than alerting on every occurrence: a steady background rate is usually a framework quirk, while a spike right after a deploy can signal a genuine infinite loop worth investigating.
#6 Handle ChunkLoadError from stale deployments
Common in single-page apps that use code splitting: the browser tries to fetch a JS chunk on demand, but the file no longer exists because a new deploy changed the chunk hashes while the user's tab was still open. Detect the mismatch and prompt a page reload rather than letting the app silently fail to render a route.
#7 Set a user identifier
Raw error counts alone can be misleading, a spike could be one frustrated user retrying the same broken action dozens of times, not dozens of different users hitting a widespread bug. Correlating errors to users lets you sort by users affected instead of raw event count, which is a far better signal of real impact.
atatus.setUser('user_email_address@domain.com');
#8 Track releases and versions
Users can leave a browser tab open for days without refreshing, so there's no guarantee an incoming error came from your latest deploy. Tagging errors with a version lets you confirm whether a bug is truly fixed in the newest release, or whether you're just watching the last errors trickle in from an old build still running in someone's browser.
atatus.setVersion('1.4.2');
#9 Capture breadcrumbs for context
A stack trace tells you where an error happened. Breadcrumbs such as clicks, navigation, console logs, and network calls leading up to it which tells you why. Without them, reproducing an intermittent bug means guessing at what the user was doing. With them, you can often see the exact sequence: which button was clicked, which API call failed, and what happened next.
#10 Group and deduplicate by fingerprint
The same underlying bug can generate thousands of individual events. Grouping errors by fingerprint such as message, stack trace, and location, collapses that into one issue with an event count, instead of a feed you have to manually sift through to find distinct problems. This is what makes it possible to triage by "how many issues do I have" rather than "how many error events fired today."
#11 Set up alerts that route by severity
Not every error deserves the same response. Configure alert thresholds around affected-user count rather than raw event volume, and route them accordingly; a single edge-case error on a rarely visited settings page shouldn't page the same on-call channel as a spike breaking your checkout flow. Send alerts through Slack, Teams, email, or PagerDuty depending on severity, and integrate with your issue tracker (Jira, GitHub, GitLab) so fixes get assigned without manual copy-paste.
#12 Disable console visibility in production
By default, most monitoring agents watch console activity and include it in the timeline, and console.error() calls can trigger errors automatically. If you're concerned about users seeing internal logs in devtools, turn off console visibility while keeping the context for your team:
atatus.config('YOUR_API_KEY', {
consoleDisplay: false
}).install();
Get context, not just a stack trace
Breadcrumbs, user identification, release tracking, and smart alerting built into Atatus JavaScript error monitoring.
Handling errors in React, Vue, and other SPAs
Global window.onerror and unhandledrejection listeners won't catch everything in a component-based framework , errors thrown during React's render phase, for example, don't propagate to window.onerror at all.
- React: wrap route-level or feature-level components in an error boundary and forward what it catches to your monitoring tool, so one broken component doesn't take down the whole page.
- Vue: use the app-level
errorHandlerhook to catch component render and lifecycle errors globally. - Any SPA: keep your global listeners active alongside framework-level handlers, they catch different categories of failure, and you need both.
Atatus ships a dedicated SPA monitoring agent that gives a full view of what's happening across route changes and AJAX-heavy interactions, in addition to React and Vue performance monitoring.
Common mistakes that undermine JavaScript error monitoring
Alerting on raw event count. One user's retry loop can generate hundreds of events and trigger a page for something affecting nobody else.
- No source maps, or public ones. Either you're debugging minified gibberish, or your original source is exposed to anyone with devtools open.
- Treating all errors equally. A checkout-breaking regression and a benign ResizeObserver warning shouldn't sit in the same triage queue with the same urgency.
- Only listening for
window.onerror. Misses unhandled promise rejections and framework-level render errors entirely. - No release tagging. You can't confirm a fix shipped if you can't tell which build an incoming error came from.
- Ignoring the noise instead of filtering it. Teams that get overwhelmed early often stop checking their error feed altogether, which defeats the purpose of monitoring in the first place.
Quick setup checklist
- Whitelist your production domains
- Add
crossorigin+ CORS headers for third-party scripts - Upload source maps privately (not publicly hosted)Listen for both
errorandunhandledrejection - Set a user identifier and app version on init
- Group errors by fingerprint, alert on affected users
- Wrap SPA routes in error boundaries / error
- HandlerRoute alerts by severity to Slack, Teams, or PagerDuty
Why teams use Atatus for JavaScript error monitoring?
Atatus monitors your production JavaScript application and tells you when real users hit errors with the context to fix them, not just a count of how often they happened.
- Un-minified stack traces from automatic or uploaded source maps
- Domain, bot, IP, and user-agent filtering so noise never reaches your dashboard
- Core Web Vitals + session replay correlated with each error, so you see performance impact and user actions together
- Smart alerting to Slack, Teams, Email, or PagerDuty, based on severity and affected users
- Issue tracker integrations with Jira, GitHub, GitLab, and BugHerd
- Two lines of code to get started; no build pipeline changes required
It's part of a full-stack observability platform that also covers APM, logs, infrastructure, and synthetic monitoring, so a frontend error and the backend API call behind it show up in the same place.
Stop guessing which JavaScript errors matter
Set up Atatus in minutes and start seeing user-impacting errors with full context
Frequently asked questions
1) Why do I see so many "Script error" messages with no useful detail?
The browser's same-origin policy hides error detail for cross-origin scripts. Add crossorigin="anonymous" to the script tag and serve the file with an Access-Control-Allow-Origin header.
2) Should I monitor unhandled promise rejections separately from thrown errors?
Yes. Unhandled promise rejections don't trigger window.onerror in most browsers, so a setup that only listens for the error event misses failed async calls and rejected fetches. Add an unhandledrejection listener too.
3) What causes ChunkLoadError in single-page applications?
A code-split bundle fails to fetch, usually because a new deployment changed chunk hashes while a user's browser still has the old build cached or open. Detect the version mismatch and prompt a reload.
4) How do I stop error monitoring from paging me for every occurrence?
Group errors by fingerprint instead of alerting per event, set thresholds on affected-user count rather than raw volume, and route alerts by severity so low-impact errors don't page the same channel as major regressions.
5) What's the difference between JavaScript error monitoring and RUM?
Error monitoring focuses on capturing and diagnosing runtime errors. Real user monitoring is broader, it also covers page load speed, Core Web Vitals, and AJAX performance. Most modern setups run error tracking as part of a RUM tool so errors can be correlated with the performance data around them.
6) How do I track errors in a React or Vue single-page application?
Global listeners miss errors thrown during React's render phase. Wrap components in an error boundary (React) or use the app-level errorHandler hook (Vue), and forward what's caught to your monitoring tool alongside your global listeners.
#1 Solution for Logs, Traces & Metrics
APM
Kubernetes
Logs
Synthetics
RUM
Serverless
Security
More