Detect recovered Capacitor WebView crashes, restart dead WebViews natively, and optionally recycle long-running WebViews on a fixed interval before memory pressure turns into an OOM.
- Stores a native crash marker when Android reports
onRenderProcessGone. - Hooks the iOS WebView termination callback and persists equivalent crash metadata.
- Restarts the WebView natively after crashes so recovery does not depend on a still-running JavaScript runtime.
- Can restart the WebView on a fixed native interval for kiosk, POS, signage, telemetry, and other always-on apps.
- Lets JavaScript request a native WebView restart with
restartWebView()when the app wants to proactively recycle memory. - Exposes the marker through an event, a polling method, and a simulation helper for testing recovery flows.
- Ships a web implementation that simulates the same recovery flow with local storage.
- Prevent the underlying WebView crash from happening.
- Restore lost in-memory JavaScript state automatically.
- Replace application-level state persistence. Persist critical state before enabling scheduled restarts.
| Plugin version | Capacitor compatibility | Maintained |
|---|---|---|
| v8.*.* | v8.*.* | ✅ |
| v7.*.* | v7.*.* | On demand |
| v6.*.* | v6.*.* | On demand |
Policy:
- New plugins start at version
8.0.0(Capacitor 8 baseline). - Backward compatibility for older Capacitor majors is supported on demand.
npm install @capgo/capacitor-webview-crash
npx cap syncimport { WebViewCrash } from '@capgo/capacitor-webview-crash';
await WebViewCrash.addListener('webViewRestoredAfterCrash', async (info) => {
console.log('Recovered after a WebView crash', info);
await WebViewCrash.clearPendingCrashInfo();
});
await WebViewCrash.addListener('webViewRestoredAfterRestart', async (info) => {
console.log('Recovered after a native WebView restart', info);
await WebViewCrash.clearPendingCrashInfo();
});
const pending = await WebViewCrash.getPendingCrashInfo();
if (pending.value) {
console.log('Pending crash or restart marker', pending.value);
}Use simulateCrashRecovery() in development or automated tests to exercise your recovery UI without forcing a real native WebView crash.
Call restartWebView() when the current JavaScript runtime decides the native WebView should be replaced:
await WebViewCrash.restartWebView();The call writes a pending marker with reason: 'manualRestart', then native code restarts the WebView. Android recreates the host Activity. iOS rebuilds the Capacitor bridge view so a new WKWebView is created instead of reloading the current page.
Configure the plugin in capacitor.config.ts so restart decisions happen in native code, even when the JavaScript runtime is unavailable:
import type { CapacitorConfig } from '@capacitor/cli';
import type { WebViewCrashPluginConfig } from '@capgo/capacitor-webview-crash';
const webViewCrash: WebViewCrashPluginConfig = {
// Keep native crash recovery enabled. This is the default.
restartOnCrash: true,
// Use a 5-field cron schedule in the device local timezone.
// Do not combine restartCron with an active restartIntervalMs.
restartCron: '0 3 * * *',
// Optional delay before restarting after a crash.
restartAfterCrashDelayMs: 0,
};
const config: CapacitorConfig = {
plugins: {
WebViewCrash: webViewCrash,
},
};
export default config;Use scheduled restarts for apps that stay open for days: kiosk screens, control-room dashboards, point-of-sale terminals, warehouse scanners, vehicle tablets, or any Capacitor app that cannot rely on users force-closing it. The restart is native, writes a pending marker with reason: 'periodicRestart', and then creates a fresh WebView.
Set restartIntervalMs to a maintenance window that your product can tolerate, or set restartCron for a wall-clock schedule such as 0 3 * * * for a daily 03:00 restart. restartCron uses 5-field cron syntax in the device local timezone and supports *, lists, ranges, and steps. Do not configure both schedules at once: native initialization throws a fatal config error when restartCron is set and restartIntervalMs is greater than 0. The user will get a fresh JavaScript runtime, so persist unsaved form state, queued events, and in-progress work before enabling a short interval or cron schedule.
export interface WebViewCrashPluginConfig {
restartOnCrash?: boolean;
restartIntervalMs?: number;
restartCron?: string;
restartAfterCrashDelayMs?: number;
}- iOS: Uses method swizzling on Capacitor's
WebViewDelegationHandlerto persist crash metadata before Capacitor reloads the WebView. Manual and scheduled restarts rebuild the Capacitor bridge view so a newWKWebViewinstance is created. No extra permissions are required. - Android: Registers a Capacitor
WebViewListenerand persists crash metadata fromonRenderProcessGone. Crash and scheduled restarts reset the bridge and recreate the host activity, giving the app a fresh WebView. No extra permissions are required. - Web: There is no real browser crash detection. The web implementation only simulates the recovery flow with local storage.
getPendingCrashInfo()clearPendingCrashInfo()simulateCrashRecovery()restartWebView()addListener('webViewRestoredAfterCrash' | 'webViewRestoredAfterRestart', ...)removeAllListeners()- Interfaces
- Type Aliases
Capacitor API for recovered WebView crash and restart detection.
getPendingCrashInfo() => Promise<PendingCrashInfoResult>Returns the pending native crash or restart marker, if one exists.
Returns: Promise<PendingCrashInfoResult>
clearPendingCrashInfo() => Promise<void>Clears the stored marker after the app has handled recovery.
simulateCrashRecovery() => Promise<PendingCrashInfoResult>Creates a fake crash marker so recovery flows can be tested locally.
Returns: Promise<PendingCrashInfoResult>
restartWebView() => Promise<PendingCrashInfoResult>Stores a manual restart marker and asks native code to create a fresh WebView.
On Android this recreates the host Activity. On iOS this rebuilds the Capacitor bridge view so a new WKWebView
instance is created instead of reloading the current page.
Returns: Promise<PendingCrashInfoResult>
addListener(eventName: 'webViewRestoredAfterCrash' | 'webViewRestoredAfterRestart', listenerFunc: (info: WebViewCrashInfo) => void) => Promise<PluginListenerHandle>Fires after a new JavaScript runtime attaches a listener and a matching marker is still pending.
| Param | Type |
|---|---|
eventName |
'webViewRestoredAfterCrash' | 'webViewRestoredAfterRestart' |
listenerFunc |
(info: WebViewCrashInfo) => void |
Returns: Promise<PluginListenerHandle>
removeAllListeners() => Promise<void>Removes all plugin listeners.
Pending crash or restart marker returned to JavaScript.
| Prop | Type | Description |
|---|---|---|
value |
WebViewCrashInfo | null |
Stored crash or restart metadata, or null when no marker is pending. |
Metadata captured natively after the previous WebView process died or was restarted.
| Prop | Type | Description |
|---|---|---|
platform |
WebViewCrashPlatform |
Platform that detected and stored the marker. |
timestamp |
number |
Unix timestamp in milliseconds for when the marker was written. |
timestampISO |
string |
ISO-8601 version of timestamp. |
reason |
WebViewCrashReason |
Platform-specific reason for the crash or restart marker. |
url |
string |
Last known WebView URL when the marker was written. |
didCrash |
boolean |
Android-only hint from RenderProcessGoneDetail.didCrash(). |
rendererPriorityAtExit |
number |
Android-only renderer priority reported at exit. |
appState |
WebViewCrashAppState |
iOS-only application state captured when the marker was written. |
| Prop | Type |
|---|---|
remove |
() => Promise<void> |
Platform that produced the stored marker.
'android' | 'ios' | 'web'
Native reason reported for the previous WebView failure or restart.
'renderProcessGone' | 'webContentProcessDidTerminate' | 'periodicRestart' | 'manualRestart' | 'simulated'
Best-effort application state captured on iOS when the WebView process died.
'active' | 'inactive' | 'background' | 'unknown'