Install — Mobile
Install AnyReplay in a Capacitor or Ionic App
Record Capacitor and Ionic app sessions with @anyreplay/capacitor: the browser SDK plus the app id, durable storage and the app's own styles and images.
A Capacitor app's screens are a web page inside a native shell, so AnyReplay records them as one — with the browser SDK, its masking and every one of its options. The Capacitor plugin adds what only the native side knows: the app id, the app version and device model, storage that survives the web view clearing its own, a flush when the app goes to the background, and the app's own styles, fonts and images in the replay.
Requirements
- Capacitor 7 or 8 (Ionic Angular, React and Vue all work the same way).
- iOS 14 or later, Android 6 (API 23) or later.
Tested with Capacitor 8.5 on iOS. Android is supported and not yet tested on a device; tell us through the contact page if anything is off there.
Install
npm install @anyreplay/capacitor
npx cap syncpnpm add @anyreplay/capacitor
npx cap syncyarn add @anyreplay/capacitor
npx cap syncnpx cap sync links the native part: with Swift Package Manager on Capacitor 8, with CocoaPods on Capacitor 7 projects that use it, and with Gradle on Android. Nothing goes into capacitor.config.ts.
Start recording
import { AnyReplay } from '@anyreplay/capacitor';
// The app id (bundle id / package name) is read from the app itself.
AnyReplay.init({
projectKey: 'YOUR_PROJECT_KEY',
ingestUrl: 'https://in.anyreplay.com',
// Stop recording after this many sessions in a calendar month.
// maxSessionsPerMonth: 10000,
// Form fields are masked: what visitors type replays as asterisks.
// Passwords, one-time codes and card fields are masked either way.
// Uncomment the first line to record what visitors type, or the
// second to hide rich-text editors too.
// maskAllInputs: false,
// maskAllTyping: true
});init returns a promise, because it asks the native side for the app id first. Everything else — consent, identify, track, trackError, flush, stop — may be called before it has finished and is applied, in order, the moment the recorder exists. A second init is ignored, so a hot reload does not start a second recorder.
import { AnyReplay } from '@anyreplay/capacitor';
AnyReplay.consent(true); // with requireConsent: true
AnyReplay.identify({ userId: 'u_1842' });
AnyReplay.track('checkout_started', { cart: 3 });Allowed apps
A Capacitor app has no web origin to list: iOS sends capacitor://localhost and Android https://localhost. The plugin sends the app's bundle id (iOS) or package name (Android) as appId instead, read from the app itself. If the project lists allowed domains or apps, add that id under Allowed apps; pass appId to init only to override it.
Styles, fonts and images
Files the app ships live at addresses nobody else can load, so a replay that refers to them by URL would be bare HTML. In an app the recorder carries them instead (assets: 'inline', the default here):
- Stylesheets are copied into the recording as text.
- PNG, JPEG, GIF and WebP images up to 3 MB are uploaded once per project, by content hash, and shown in the replay.
- Fonts up to 160 KB and SVG images up to 48 KB travel inside the recording, up to 1 MB per page.
- Files from the device — photos shown through
Capacitor.convertFileSrc— are never read.
A file over those limits is missing from the replay and nothing else changes. Images from the internet are recorded by URL, as on a website.
What the native side does
| iOS | Android | |
|---|---|---|
| App id | Bundle.main.bundleIdentifier | context.packageName |
| Version | CFBundleShortVersionString | versionName |
| Device model | iPhone15,2 | Google Pixel 8 |
| Storage | UserDefaults | SharedPreferences |
| Background | Flush inside a background task of at most 10 seconds | Flush on pause |
The storage holds a copy of the recorder's own keys — the visitor id and the current session — so WKWebView clearing its storage does not make a returning user new, and a relaunch within 30 minutes continues the session. consent(false) clears that copy too. The plugin asks for no permissions and makes no network requests of its own.
Known limits
- Unsent events are kept in memory while the device is offline; if the app is killed before it reconnects they are lost.
- Native screens shown on top of the web view (a native camera, a payment sheet) are not in the page, so they are not in the replay.
Every option is in the browser SDK reference. For a Cordova app see Cordova.