Kilian Valkhof

Front-end & user experience developer, Jedi.

Using Google Analytics to gather usage statistics in Electron

Apps, 24 April 2018, 4 minute read

The Electron App framework makes it really easy to build cross-platform applications. I know this, because I’ve made a bunch. But how do you find out if people are using the features in your application? You could ask them or wait for them to tell you, but you can also use Google Analytics’ event tracking to find out.

A little note on privacy: It should be obvious that you should tell your app users that you’re doing this, and if you’re smart (and with the GDPR in mind) you only collect anonymous data. Track the interactions in your app, don’t collect user data. That said, let’s get to it:

If you’re running Electron, you probably want to track events in both the renderer process (the browser part) as well as the main process (the node part). That means you can’t use the regular Google Analytics script, which only supports the browser part. Instead, You need universal-analytics, an NPM package that can run in your main process. (universal-analytics can also track screenviews in your application, but for this article we’re focusing on events.) Install it using

npm install --save universal-analytics

Setting up Analytics in the main process

Once you’ve installed it, you can import it in your main process and set it up.

const ua = require('universal-analytics');

Before you can start using it, you need to think about what kind of data matters to you. Universal-analytics needs a user id to generate statistics. Without it, it will generate a random user every time your application starts. This works if you only care about tracking events but if you would also like to know how many users you have and how often they use your application, then you need to generate and keep using the same id for a user. Here’s how I do that:

const uuid = require('uuid/v4');
const { JSONStorage } = require('node-localstorage');
const nodeStorage = new JSONStorage(app.getPath('userData'));

// Retrieve the userid value, and if it's not there, assign it a new uuid.
const userId = nodeStorage.getItem('userid') || uuid();

// (re)save the userid, so it persists for the next app session.
nodeStorage.setItem('userid', userId);

I use two NPM packages for this: uuid to generate a random unique ID (This keeps the users anonymous, as opposed to using the OS username, IP address or MAC address or something similar) and I use node-localstorage to store and retrieve the user id so it persists between app uses.

With this user id sorted, you can now initialize your universal-analytics. The first argument is your Google Analytics Tracking ID, and the second is your just generated userId. If you don’t care about persistent users, then you can initialize universal-analytics with just your Tracking ID.

const usr = ua('UA-XXXXXXXX-X', userId);

Now you can use this usr variable to track events with. The easiest way to do this is to create your own trackEvent function:

function trackEvent(category, action, label, value) {
  usr
    .event({
      ec: category,
      ea: action,
      el: label,
      ev: value,
    })
    .send();
}

This function creates an event and sends it off to google analytics. It can accept a category, action, label and value. To understand what they do, check out the Analytics documentation on events. Category and action are the only two required arguments. If you don’t add a third and fourth argument to the function, then they will resolve to undefined and won’t be part of your event data on Google Analytics.

At this point, we have quite a bit of code in our main process file, and it’s time to extract that out to an external file. So create a file called analytics.js, and copy all the above into it:

const ua = require('universal-analytics');
const uuid = require('uuid/v4');
const { JSONStorage } = require('node-localstorage');
const nodeStorage = new JSONStorage(app.getPath('userData'));

// Retrieve the userid value, and if it's not there, assign it a new uuid.
const userId = nodeStorage.getItem('userid') || uuid();

// (re)save the userid, so it persists for the next app session.
nodeStorage.setItem('userid', userId);

function trackEvent(category, action, label, value) {
  usr
    .event({
      ec: category,
      ea: action,
      el: label,
      ev: value,
    })
    .send();
}

Then at the end of that file, export the trackEvent function like this:

module.exports = { trackEvent };

And import it into your main process:

const { trackEvent } = require('./analytics');

This makes your trackEvent function available in your main process. Call it when the user does something that you want to keep track of, with at least a category and an actions. For example, in Polypane, I keep track of how often screenshots are generated:

trackEvent('User Interaction', 'Screenshot created');

Then in Google Analytics I get the total number of screenshots created over a period of time, allowing me to see how popular this particular function is.

Using Analytics in the renderer process

Not all the events you track are going to be in the main process. The most will probably happen in your renderer process, where your GUI is. To track events there too, you want to share your trackEvent function with the renderer process. To do this, add it to the global variable in your main process:

global.trackEvent = trackEvent;

Then, whenever you want to track an event in your renderer process, put this at the top of your file(s):

const { getGlobal } = require('electron').remote;
const trackEvent = getGlobal('trackEvent');

And with this bit of plumbing, you’re done!

Calling the function works the same in both the renderer and the main process. Now you can track user interactions throughout your Electron application and get useful quantified feedback on the functions your users use. Let me know if you use this in interesting ways!

Thanks for Reading!

I am Kilian Valkhof, a front-end and user experience developer from the Netherlands.
Contact me or ping me on twitter.

Be the first to know about new releases!

Newsletter subscribers will be the first to know about new apps I release or new articles I write, here or elsewhere.

Low-volume. I will only email you when I have something exciting to share.