An Electron app is not a web page even though it is built from one, so there is no script tag here. There is a package and one call per process. See measuring an app for what that gets you. The Skomi Electron package depends on nothing but Electron itself.

The source, and a demo app that reports itself, are at skomi-sdk-electron.

Three processes, three lines

// main.js — start it once
const skomi = require('@skomi/electron').init({
    appId: 'your-app-id',
});

skomi.screen('launch');
// preload.js — expose it to the window
require('@skomi/electron/preload');
// renderer — wherever your routing lives
window.skomi.screen('checkout/review', { tier: 'gold' });

That is the whole integration. The app ID comes from My Apps in Skomi. It is not a secret and belongs in your source. It identifies an app rather than authenticating one, the same way a site ID does.

Do not reach for the web snippet in your renderer. It measures a site: it wants a hostname, a document to watch and a network it can rely on, and an Electron window has none of those in the way it expects. The SDK exists because a desktop app's problems are different ones.

Name your screens

There is no automatic capture, and that is deliberate: a route path is what your code calls a screen, not what you would call it in a report. Up to 20 props per screen: strings, numbers or booleans.

What the SDK handles that you would otherwise write

It works offline. Screens are queued on disk in the app's own data directory and survive quitting, crashing and a fortnight without a network. A desktop app that only reported while online would lose exactly the sessions that matter.

It does not double-count. Every screen carries an id minted once and resent unchanged, so a retried batch is recognised rather than counted again. An offline queue without that turns the thing that saves your data into the thing that invents it.

It survives a wrong clock. Each batch records when it was flushed, so the server can work out how far out the machine's clock is and correct the batch. A laptop set to next Tuesday otherwise reports next Tuesday.

It never throws into your code. Bad arguments, an unreachable server, an unwritable profile directory are all handled internally and reported through onError if you want to know. Analytics is the least important thing your application does.

const skomi = require('@skomi/electron').init({ appId: '…', enabled: false });

// later, once the user has agreed
skomi.setEnabled(true);

Nothing is recorded while disabled, and not queued for later. A screen viewed before someone agreed is not one you should be sending afterwards.

Errors and crashes

skomi.captureError(err, { screenName: 'checkout', fatal: false });

Queued, persisted and retried like a screen, and deduplicated by the same rule, which matters more here, because a crash usually gets sent from the queue on the next launch.

captureUncaught is off by default, and think before turning it on. Attaching a listener to uncaughtException is what stops Node exiting, so a crash reporter installed by default would turn a fatal crash into a process that keeps running on corrupt state. With the flag on, the semantics are put back: the report is written to disk synchronously, the listener is removed, and the error is re-thrown.

Options

init({
    appId: '…',                     // required
    appVersion: app.getVersion(),   // default: asked of Electron
    origin: 'https://t.skomi.com',  // override for self-hosted
    enabled: true,                  // start false, switch on after consent
    flushIntervalMs: 10000,
    onError(error, context) { },    // 'screen' | 'flush' | 'transport' | 'rejected' | 'queue'
});

init wires the renderer channel and flushes on quit. If you want a client whose lifetime you own, with no IPC and no quit hook, use createClient instead.

What an app does not get

Only analytics reads an app. Heatmaps, session recordings and the feedback widget are site products: they need a page in a browser, and there is nothing for them to attach to in a desktop window. An app reports screens, sessions, versions, platforms and errors, and that is the whole of it.

See tracking an app for what the dashboard does with them, and installing on MAUI or Flutter if you also ship the same product there.