Event tracking a Shiny application with Google Analytics

shiny hexagon

You developed an awesome Shiny application and published it to the web. Congrats! Now you want to get a sense of who uses your application and what they do while they are there. Luckily, there is Google Analytics. In this blogpost, you will learn how to add general tracking and event tracking code to your Shiny application with Google Analytics.

First, we cover the basics of Google Analytics. Second, we discuss the requirements for adding tracking code to your application. After that, we’re going to adjust our tracking code to include event tracking. Lastly, we will get a glimpse of what the actual results look like in Google Analytics.

Google Analytics basics

A Shiny application is just a web page. Google Analytics tracks visitor behavior on every web page. So, also for Shiny applications you can unlock the power of Google Analytics.

With Google Analytics you can get information about the number of visitors, the origin of the visitors, how long they stay on your website, and much more. That’s what we call the general tracking.  

Besides the general tracking, there is event tracking. With event tracking you track specific events. Events are user interactions with content which can be measured independently from a web page. Think about downloads, mobile ad clicks, gadgets, Flash elements,  and video plays.  For Shiny applications you can think about user interactions like selecting inputs and clicking buttons.

While there are great tutorials out there on how to implement general tracking in your Shiny application, there is not much information about implementing event tracking. Therefore, the focus in this blog post is on event tracking.


In this paragraph, we’re going to lay out the requirements. Firstly, in order to use Google Analytics, you need a free account on google.com/analytics. Next, you must register your Shiny application as a property in your Google Analytics account. You can do this on the sign up page, or, if you already have an account, on the “Admin” tab. For a more extensive tutorial on how to do this you can go to the Shiny website. 

After you provide information about your property, Google Analytics opens a new window that contains a tracking ID number as well as a short JavaScript snippet. The JavaScript looks similar to this:

<!-- Global site tag (gtag.js) - Google Analytics -->
<script async src="https://www.googletagmanager.com/gtag/js?id=UA-12345678-1"></script>
  window.dataLayer = window.dataLayer || [];
  function gtag(){dataLayer.push(arguments);}
  gtag('js', new Date());

  gtag('config', 'UA-12345678-1');

Copy the snippet and save it as .html in the working directory of your application. You can call it “google-analytics.html”, or any other name you prefer.

Your Shiny application

Your code snippet is in your working directory now, so let’s actually embed it! 

To show you how to embed your tracking code, we use an example from the Shiny library. The Shiny Text application is a simple application that takes two inputs and prints a summary of the selected dataset. You can run the example yourself with the code below.


Next, to embed the “google-analytics.html” file into your Shiny application we can use tags$head. Here the HTML can be be included. In the example below that looks like: tags$head(includeHTML(("google-analytics.html"))).

# Define UI for dataset viewer app ----
ui <- fluidPage(
  # Include tracking code ----
  # App title ----
  titlePanel("Shiny Text"),
  # Sidebar layout with a input and output definitions ----
    # Sidebar panel for inputs ----
      # Input: Selector for choosing dataset ----
      selectInput(inputId = "dataset",
                  label = "Choose a dataset:",
                  choices = c("rock", "pressure", "cars")),
      # Input: Numeric entry for number of obs to view ----
      numericInput(inputId = "obs",
                   label = "Number of observations to view:",
                   value = 10)
    # Main panel for displaying outputs ----
      # Output: Verbatim text for data summary ----
      # Output: HTML table with requested number of observations ----

# Define server logic to summarize and view selected dataset ----
server <- function(input, output, session) {
  # Return the requested dataset ----
  datasetInput <- reactive({
           "rock" = rock,
           "pressure" = pressure,
           "cars" = cars)
  # Generate a summary of the dataset ----
  output$summary <- renderPrint({
    dataset <- datasetInput()
  # Show the first "n" observations ----
  output$view <- renderTable({
    head(datasetInput(), n = input$obs)

shinyApp(ui = ui, server = server)

Basically, there are two inputs the user can select: input$dataset and input$obs. The first input is where the user selects the desired dataset. This dataset can be either “rock”, “cars”, or “pressure”. The second input is used to display the number of observations. Our goal for this tutorial is to show which dataset the users select and how many observations they choose.

Add event tracking to your Shiny application

The dataset (“rock”, “pressure” or “cars”) is selected via a selector (selectInput). The number of observations is inputted via a numeric input field (numericInput). Thus, we are interested in inputs of the elements select and input. We need these elements in our tracking code. For example, if we reference the dataset selector, we use select#dataset, where dataset is the inputId of selectInput in our Shiny app.

Furthermore, we want to track both events on change. Whenever the user changes the selected dataset or the number of observations, we want to know. That’s why we use change in our code snippet. Another possibility would be to track an event on click.

Below you can see how we changed the “google-analytics.html” file to include event tracking.

<!-- Global site tag (gtag.js) - Google Analytics -->
<script async src="https://www.googletagmanager.com/gtag/js?id=UA-12345678-1"></script>
  window.dataLayer = window.dataLayer || [];
  function gtag(){dataLayer.push(arguments);}
  gtag('js', new Date());
  $(document).on('change', 'select#dataset', function(e) {
    gtag('event', 'change', {'event_category' : 'choose dataset',
    'event_label' : document.querySelector('select#dataset').value

  $(document).on('change', 'input#obs', function(e) {
     gtag('event', 'change', {'event_category' : 'no. observations',
    'event_label' : document.querySelector('input#obs').value

  gtag('config', 'UA-12345678-1');

The event_label is the label that the event will get in Google Analytics. For our purpose, we use the value of selectInput and numericInput as label.

Results in Google Analytics

Once the tracking code is saved in the working directory of your app folder, you can go to Google Analytics to see the results. The fun begins!

You can analyze events in two ways. The first way is with real-time tracking. This allows you to see in real-time what users are selecting. The second way is the “Behavior” > “Events” panel.

Google Analytics real-time tracking

In the example above, we see real-time event tracking. There is 1 active users on site and the user did some change events related to the dataset that was chosen (“cars”, “pressure” or “rock” our event_label). That’s cool! Real-time event tracking only goes back 30 minutes though.

Via “Behavior” > “Events” you get more insight in the number of events over a certain time range. This allows you to look further back than just 30 minutes. It might take some time for events to show up there, as there is some processing time on the side of Google Analytics.

For any given time range you can get information about the total number of events for a certain label. In the case below the label represents the chosen dataset:

But what about the number of observations? In the screenshot below you can see the results for the event tracking of the numeric input. Most users selected 19 as the number of rows to show, followed by 20 and 18. This can be valuable information to optimize the users experience of your application. For instance, if your default value of the number of observations is 5, you might want to change that to 20.

Unfortunately, we can’t show everything in just one blog post. Google Analytics can do a lot more. You just have to know where to find it and how to implement it. For example, take a look at this awesome resource about the use of Google Analytics, and event tracking especially.


This blog post showed you how to add event tracking to your Shiny application with Google Analytics. It’s nice to have general information about your users, but it’s even more awesome if you know what they are doing exactly. Hopefully you now have a starting point to implement event tracking yourself!

Happy tracking!

PS: want to see more blog posts? Head over to the blog to read more!

4 thoughts on “Event tracking a Shiny application with Google Analytics”

  1. Thanks, Veerle. My question: is it possible to add event tracking to our Shiny application if the app was published serverless by using shinylive?

    1. Veerle van Leemput

      Hi Yosep, yes! You just need to add some JS, so there’s no server side processing needed- this should work perfectly fine with shinylive 🙂

      1. Thanks for your response. My app is at https://people.usd.ac.id/~ydkristanto/app/simulasi-tabungan/. When I write the script inside the tags$head of the app, Google Analytics (GA) doesn’t detect it but when I write it inside the head of index.html, GA detect it. However, when the script is placed inside the index.html, GA doesn’t track the event that I define in the script. All events is detected as ‘user_engagement’. Is it because the Shiny app is contained inside the iframe?

        1. Veerle van Leemput

          If I look at your app, I can see that the GA tag is in the head of the HTML document. If you take a closer look at the HTML, you’ll see that it indeed contains an iframe, and in that iframe there’s another HTML document. GA will not track events in another document, so there’s a mismatch in scope. I’m not sure why it wouldn’t work to place the GA tag in the app itself though.

Leave a Reply

Your email address will not be published. Required fields are marked *