Wouldn't it be great if you could easily figure out what users are doing when they come to your Ghost blog? Like, without having to understand or touch a bunch of code? Are they sharing your post on social sites? Are they following menu links? Now you can know. And knowing is half the battle.

GI Joe

(If you already know how to work with Google Tag Manager or you've already got it set up, skip to the actual tag, trigger, and variable setup.)

It's fairly straightforward to add Google Analytics tracking to your Ghost blog now that they've added the "Code Injection" section of the admin area. Just create a new Google Analytics account for your website and paste the install script into the code injection section, and you'll be tracking page views on your site. Mine looks like this:

Google Analytics Code Injection

The only thing that will be different for you is your GA Property (starting with "UA-").

If you want to do more complex tracking, however, it can be difficult to get started. If you're using a hosted Ghost blog (Ghost Pro), you won't be able to edit the code directly, and if you're self-hosting and have access to the code, you may not want to go changing things because that can make it more difficult to upgrade to a new version later (merge conflicts). But there is an answer. Use GTM.

Google Tag Manager

Google Tag Manager is a Google service intended to help non-developers interact with code without needing to touch the code. How does this work? (Warning: semi-technical explanation follows). GTM watches the state of a variable on the page (called the "dataLayer"), and when the state of that variable changes in ways that you've defined (by something called "triggers"), GTM injects "tags" onto your page. A "tag" can be arbitrary HTML, an image, a Google survey, a Google Analytics event, etc. I recently set up GTM to track a variety of actions on this blog, such as opening and closing the menu, sharing a post, and following a link from a post. Here, I'll share those with you so you can get better insight into what users are doing on your blog.


The first thing you'll need to do is create a Google Tag Manager account. Visit https://tagmanager.google.com and login with your gmail account (or create a new one). If you already have a Google Tag Manager account, you should see something like this:

GTM Account Page

with your existing accounts listed. Click on "Create Account," and you should see this:

GTM Create Account Part 1

If you don't have a Google Tag Manager account, when you login, you should land here. Type your "company" name (mine is "Stubborn Child Process," the name of this blog) and click "Continue." Then enter the website of your blog and click "Web," then "Create":

GTM Create Account Part 2

At this point, you should get a popup with an install script (you may get an "Agree to our terms" popup first). This is exactly like the install script for Google Analytics.

GTM Install Script

Copy this and paste it into your Ghost Code Injection block below the Google Analytics snippet, but delete the <noscript> tag at the beginning (everything between <noscript> and </noscript>). Normally GTM wants you to put your install code in the body of the page, where <noscript> is valid, but Ghost only let's you inject scripts in the head or footer. Either one will actually work, but it's better to execute your tag manager code toward the top of the page where it will start picking up triggers right away.

While you're here editing your code injection section, you need to add one more thing to that that block; you need to set up your "dataLayer." This is a JavaScript variable that is an array. If you don't need any GTM variables, you can just define it like this:

dataLayer = [];  

but we're going to define it with a what's called a "data layer variable":

dataLayer = [{  
  gaProperty: 'Your Google Analytics property id'

This tells GA that we want to be able to use this value within GTM tags and triggers. Here's the full contents of my code injection section after completing this step:

        (i[r].q=i[r].q||[]).push(arguments)},i[r].l=1*new Date();a=s.createElement(o),

    ga('create', 'UA-54037723-3', 'auto');
    ga('send', 'pageview');


    dataLayer = [{
      gaProperty: 'UA-54037723-3'

<!-- Google Tag Manager -->  
new Date().getTime(),event:'gtm.js'});var f=d.getElementsByTagName(s)[0],  
<!-- End Google Tag Manager -->  

Now that GTM is set up, we can start configuring our analytics events.

Tags, Trigger, and Variables (Oh My)

Tags, triggers, and variables are the three main components of Google Tag Manager. I'm not going to go into a detailed explanation of them here, but in short:

  • Variables are reusable values that can be added to tags and triggers.
  • Triggers are things that happen on the page that you want to react to
  • Tags are scripts or images (or whatever) that you want to inject in reaction to a trigger


We'll start by adding a simple variable for the Google Analytics property id we set up in our data layer earlier. Go to "Variables" in the left nav, choose "New," and choose "Data Layer Varaible." Type "gaProperty" as the name and as the "Data Layer Variable Name." This tells GTM that we want to store the "gaProperty" we're putting in our data layer for use in tags and triggers.

GA Property Variable

Of course, you wouldn't have to do this using a data layer variable. You could just paste your Google Analytics property id into GTM, but this way, all of your Google service ids (GA and GTM) are in one place: your Ghost blog code injection block.

Save the variable. Really, that's all there is to adding a variable. They can be more complicated of course, but only if you're trying to do more complicated things. We'll see an example of a more complicated one later.

Tracking the Menu

Now that we've got our Google Analytics property id available in GTM, we can start adding tracking events. Let's start with the menu. I am personally a big fan of the new Ghost menu. I use it to link to things like Github, Twitter, and Stack Overflow, but I currently have no way of knowing if someone clicks on one those links. Let's fix that. First, in the Variables section, you'll see that there are some predefined variables listed at the top, but most are not turned on by default. We need to enable "Click Element," "Click Classes," "Click URL," and "Click Text" for the following tags to work. In short, these are variables that GTM sets based on properties of things that are clicked on your website.

The top of your variable section should now look like this:


Now go to "Triggers" and click "New." Call the trigger "Nav Open" (or "Menu Open" or something that tells you what it is). Under "Choose Event," select "Click." Under "Configure Trigger," choose "Just Links" from the dropdown. Presumably, "Just Links" is more performant than "All Elements," but . . . I really have no idea. But the element we want to monitor clicks on is a link, so it feels like this is the right choice. Under "Fire On," choose "Some Clicks" and then select "Click Classes" and "contains" and type "menu-button icon-menu" in the third box. This means that links with the classes "menu-button" and "icon-menu" (the classes on the menu button in Ghost) will activate this trigger and fire any tags associated with it. Your trigger should look like this:

Nav Open Trigger

Click "Save Trigger." Notice, too, the helpful "Copy" on the right hand side. This is really handy when you're creating a number of very similar triggers (or tags). Let's go ahead and add a "Nav Close" trigger and a "Nav Element Click trigger" while we're here. You can either copy the "Nav Open" trigger or create new ones. Configure them like this:

Nav Close Trigger

Nav Element Click Trigger

You'll notice that "Nav Element Click" uses a slightly different "Fire On." We're using "Click Element" and "matches CSS selector," which is a really flexible way to trigger tags when either the class is not unique or you want to make a generic trigger that will fire on multiple links. In this case, we're doing the latter. All the nav links match this CSS selector path, and we'll use another trick in our tag to differentiate the Google Analytics events we fire. You could instead create specific triggers for each Nav link you have if you really wanted, but this is more tedious, and if you add new nav links, you'll have to come add new triggers, whereas this approach will work going forward for new nav links.

Let's add the corresponding tags for these triggers now, and then come back to some other triggers. In the "Tags" section, click "New." Call it "Nav Open Event" and choose "Google Analytics" from the list of prebuilt options. Under "Choose a Tag Type," choose "Universal Analytics." If, for some reason, you're using the old "Google Analytics" style tracking, you can select that, but the steps that follow may be different for you. Under "Configure Tag," in the "Tracking ID" field, click the the little lego block beside the input box (this brings up the variable list). This will give you a list of all the variables you have enabled for your container. Find "gaProperty" in the list (the one we created earlier). Set "Track Type" to "Event," and then set "Category" to "Menu," and "Action" to "Open." For "Label," use the lego block again and select "Page Path" (so you can see on which pages your menu gets opened most often). Set "Value" to 1 (although I believe this is the default if you leave it unset). Under "Fire On," choose "Click" and choose your "Nav Open" trigger from the popup. The final trigger should look like this:

Nav Open Event

Next add a "Nav Close Event" and a "Nav Element Event" with this configuration:

Nav Close Event

Nav Element Event

Notice on the "Nav Element Event" tag, the "Action" is set to "{{Click Text}}." This is how we differentiate which link was clicked, despite using the same trigger for all the nav links. If you have a link to your twitter account in the menu, with the text "Twitter," this event with have an action of "Twitter." On a side note, if you are unfamiliar with Google Analytics, "Category," "Action," and "Label" are arbitrary titles for these properties, in case you were thinking that "Twitter" didn't sound like an action.

Tracking Tags

Ghost uses tags to organize your posts into useful categories, so that people who read a post on one topic and want to read more could do so. Let's add similar tracking to our tags. At this point, I'm going to assume you understand some of the fundamentals of adding tags and triggers and just show the final configuration for them.

Add this trigger, which fires on all tag clicks

Tag Trigger

and this tag to fire your Google Analytics event.

Tag Tag

I also do a lot of external linking within my posts, so I wanted to see who was clicking on those links. Here's the trigger for this:

Post Link Trigger

The full CSS selector (which you can't see in the image) is main.content article.post section.post-content a. This will match any anchor tag within the post body. The corresponding tag looks like this:

Post Link Tag

Note that I'm using "Click URL" as the event action. We haven't seen this yet, but it will be the href attribute of the link (or the destination url, if you will). You could also use "Click Text" here if you wanted, but I often make my click text something like "here," as in "You can find more details here." So that's obviously less useful than knowing where the link goes.

Tracking Social Shares

Finally, Ghost automatically includes these little social icons at the bottom every post at your behest.

Share This Post

I would like to know when someone shares one of my blog posts. I thought the Twitter share might at least include @tandrewnichols, which would notify me of the tweet, but it doesn't. So I added (sort of) similar tracking to this. The trigger looks like many of the others:

Share Trigger

But the tag is a little different.

Share Tag

Notice the action says {{socialService}}. These links have no text so {{Click Text}} won't work. I could do {{Click URL}}, but they end up being really long and ugly and unique (bad for easy reporting). I could also use {{Click Classes}}, which do include icon-twitter, icon-facebook, and icon-google-plus, but this is less clean than I wanted. What I really wanted was "Twitter," "Facebook," and "Google Plus." So I added a custom variable called socialService to calculate this value. You can use variables, like Click Classes in other computed variables, and then manipulate them, so I wrote a little javascript to take icon-twitter and turn it into "Twitter." Here's the code:

function() {  
    var classes = {{Click Classes}}.replace('icon-', '').split('-');
    return classes.map(function(word) {
        return word.charAt(0).toUpperCase() + word.slice(1);
    }).join(' ');

If you're unfamiliar with javascript, this removes the "icon-" part of the class, then splits on any remaining hyphens (as in "google-plus"), then capitalizes each word. All you need to know is that this will make your tracking for these elements look a little cleaner. Here's the full variable configuration:

Social Service Variable


Google Tag Manager includes a Preview Mode that let's you test your setup to make sure everything is working. This is definitely worth doing if you're figuring out your own tags (the console that opens to help you debug is fantastic). You may not need that for these tags (since I've already done it). If you choose to test, crack open Google Analytics and go to Real-Time -> Events where you should see your events show up if everything is correct. One thing I strongly suggest, if you're going to do testing, is to set up a secondary "dev" Google Analytics account and temporarily replace your primary property id with this test one (don't forget to change it back); otherwise, you'll have all your test events mixed in with your live data, and it'll be harder to distinguish real interactions from real users. Don't forget to turn preview mode off when you're done.


And that's it. Make sure you click "Publish" in the top right corner before you call it a day, otherwise your changes will not be live on your site, and you'll be wondering why no one is clicking on anything.

All in all, I was surprised how easy the whole process was. I've heard many times how GTM was intended to be a way for non-developers to do things with production code without needing the intervention of a developer, but I had never tried to set anything up without touching code. I use GTM at work too, but there, we have developers, so it's usually us who are doing some combination of actual code editing and GTM editing. Hopefully, you found this process easy whether you are a developer or not. Either way, you should now have better insight into what your readers are doing when they come to your blog.