By Rahul — Google Frontend Engineer
Why is This Hard?
When a tab crashes, your JavaScript stops running. You cannot run code to report the crash because... the code is not running anymore. It is like asking a dead person to call 911. So how do we detect it?
Method 1: Service Worker Heartbeat
The most reliable approach. Service workers run in a separate thread — they survive tab crashes.
// In your page
const HEARTBEAT_INTERVAL = 5000; // 5 seconds
// Send heartbeats to service worker
setInterval(() => {
navigator.serviceWorker.controller?.postMessage({
type: 'HEARTBEAT',
tabId: sessionId
});
}, HEARTBEAT_INTERVAL);
// In service worker
const activeTabs = new Map();
self.addEventListener('message', (event) => {
if (event.data.type === 'HEARTBEAT') {
activeTabs.set(event.data.tabId, Date.now());
}
});
// Check for dead tabs
setInterval(() => {
const now = Date.now();
for (const [tabId, lastBeat] of activeTabs) {
if (now - lastBeat > 15000) { // No heartbeat for 15s
// Tab probably crashed
reportCrash(tabId);
activeTabs.delete(tabId);
}
}
}, 10000);Method 2: sessionStorage Flag
// On page load, check if previous session ended cleanly
if (sessionStorage.getItem('loaded') === 'true'
&& !sessionStorage.getItem('unloaded')) {
// Page was loaded but never unloaded = crash!
reportCrash();
}
// Set loaded flag
sessionStorage.setItem('loaded', 'true');
sessionStorage.removeItem('unloaded');
// Set unloaded flag on clean exit
window.addEventListener('beforeunload', () => {
sessionStorage.setItem('unloaded', 'true');
});This has false positives (force quit browser, power loss) but is simple and requires no service worker.
Method 3: Reporting API
// Modern browsers support crash reports natively
// Set the Reporting-Endpoints header
Reporting-Endpoints: crash-reports="https://example.com/reports"
// Or use the ReportingObserver API
const observer = new ReportingObserver((reports) => {
for (const report of reports) {
if (report.type === 'crash') {
sendToAnalytics(report);
}
}
}, { types: ['crash'], buffered: true });
observer.observe();Common Causes of Tab Crashes
- Memory leaks: Unbounded arrays, retained DOM references, forgotten event listeners
- Infinite loops: Recursive rendering, while loops with wrong conditions
- Massive DOM: 100,000+ nodes cause the renderer to crash
- WebGL/Canvas: GPU memory exhaustion
- Out of memory: Loading huge files into memory, large base64 strings
Prevention Best Practices
- Monitor memory usage with
performance.measureUserAgentSpecificMemory() - Use virtual scrolling for large lists
- Clean up event listeners and intervals on unmount
- Set limits on user-uploaded file sizes
- Use Web Workers for heavy computation
Summary
Detecting crashes is hard because your code dies with the tab. Use service worker heartbeats for the most reliable detection, sessionStorage flags for a simpler approach, or the Reporting API for modern browsers. Prevention is better — monitor memory and avoid unbounded growth.