segmentio-js-snippet-3.1.0

in Blog

How to Install Segment’s JavaScript Snippet into a React Web App

A brief journey into how we integrated Segment’s client-side library into our app.

Note to the reader: This post is old and I intend to update it soon. React and Segment have come a long way since the first edition of this piece.

Here’s the tl;dr.

  1. Make sure Segment’s snippet is embedded on every page
  2. For page calls, remove the analytics.page() from the default Segment snippet and each time there is a route change (i.e. URL changes), make sure to invoke analytics.page(). You can do this via a React instance methods (like componentDidMount), a Higher Order Component (HOR), or with React Router, if you use it).
  3. For analytics.track() calls, add them to your event handlers (i.e. onSubmit, onClick, etc).
  4. Consider keeping all your analytics code (whether Segment or otherwise) in one file and export function wrappers. Why? Because keeping third party JS isolated is easier to manage and update, as well as remove or replace. For example:
// analytics.js
import analytics from 'segment'
export const trackFormSubmit = () => {
  analytics.track('Form Submitted', {...properties here...})
} // signup.js

// import analytics track or page calls then invoke as you wish
import {trackFormSubmit} from './analytics.js'

Here is the original post that I wrote many years ago:

Segment helps you track your important growth metrics and engagement activity with your product. It then spits out the data to your favorite marketing and analytics tools, like Google Analytics or Mixpanel. Not only does this make your code much cleaner but it will also make your life much easier when your marketing team stops bugging you to add and remove more snippets.

For our app, the three types of metrics we want to track are: pageviews, user identities, and event tracking. Fortunately, Segment has three methods in their JavaScript library to help make this happen:

Before diving into how we solved this, I should note that this article assumes you have a basic understanding of what Segment does, how their JavaScript snippet works, and how Single Page Apps (SPA) work. Also, I only address client-side tracking… stay tuned for my upcoming post on Segment/React server-side tracking.

Most importantly, I’d like to point out that our solution is imperfect. We optimized for shipping a functional MVP under a tight deadline. If given the luxury of more time, I would definitely have optimized for modularity and separation of concerns.

When working with SPAs, Segment reminds us that clicking a link or new tab will not reload the webpage. Thus, we need to remove the analytics.page() from the snippet and invoke that method when we simulate a new page load.

But if we remove the page call from our snippet, how will we send this valuable data to Segment’s servers? In order to do this, we have a few options. In Angular, you can simply attach it to the router. But my solution with React was different. Here is how I solved it.

First Solution: Install Segment’s node module as a dependency

Note to Reader: do NOT use this solution.

̶A̶ ̶b̶r̶i̶e̶f̶ ̶G̶o̶o̶g̶l̶e̶ ̶s̶e̶a̶r̶c̶h̶ ̶l̶e̶d̶ ̶m̶e̶ ̶t̶o̶ ̶d̶i̶s̶c̶o̶v̶e̶r̶ ̶t̶h̶a̶t̶ ̶S̶e̶g̶m̶e̶n̶t̶ ̶h̶a̶s̶ ̶a̶ ̶n̶p̶m̶ ̶m̶o̶d̶u̶l̶e̶.̶ ̶I̶ ̶d̶e̶c̶i̶d̶e̶d̶ ̶t̶o̶ ̶s̶t̶a̶r̶t̶ ̶h̶e̶r̶e̶.̶

̶U̶n̶f̶o̶r̶t̶u̶n̶a̶t̶e̶l̶y̶,̶ ̶w̶e̶ ̶a̶r̶e̶ ̶u̶s̶i̶n̶g̶ ̶w̶e̶b̶p̶a̶c̶k̶ ̶t̶o̶ ̶b̶u̶i̶l̶d̶ ̶o̶u̶r̶ ̶a̶p̶p̶ ̶a̶n̶d̶ ̶a̶s̶ ̶t̶h̶e̶ ̶S̶e̶g̶m̶e̶n̶t̶ ̶S̶u̶c̶c̶e̶s̶s̶ ̶T̶e̶a̶m̶ ̶p̶o̶i̶n̶t̶e̶d̶ ̶o̶u̶t̶,̶ ̶w̶e̶b̶p̶a̶c̶k̶ ̶d̶o̶e̶s̶n̶’̶t̶ ̶h̶a̶v̶e̶ ̶a̶ ̶d̶e̶f̶a̶u̶l̶t̶ ̶s̶h̶i̶m̶ ̶f̶o̶r̶ ̶c̶e̶r̶t̶a̶i̶n̶ ̶n̶o̶d̶e̶ ̶m̶o̶d̶u̶l̶e̶s̶,̶ ̶w̶h̶i̶c̶h̶ ̶w̶a̶s̶ ̶c̶a̶u̶s̶i̶n̶g̶ ̶o̶u̶r̶ ̶b̶u̶i̶l̶d̶ ̶t̶o̶ ̶f̶a̶i̶l̶.̶
̶E̶x̶c̶e̶p̶t̶ ̶f̶o̶r̶ ̶t̶h̶i̶s̶ ̶w̶e̶b̶p̶a̶c̶k̶ ̶i̶s̶s̶u̶e̶,̶ ̶I̶ ̶w̶o̶u̶l̶d̶ ̶d̶e̶f̶i̶n̶i̶t̶e̶l̶y̶ ̶h̶a̶v̶e̶ ̶p̶r̶e̶f̶e̶r̶r̶e̶d̶ ̶t̶o̶ ̶g̶o̶ ̶t̶h̶i̶s̶ ̶r̶o̶u̶t̶e̶.̶ ̶I̶’̶l̶l̶ ̶p̶l̶a̶n̶ ̶t̶o̶ ̶l̶o̶o̶k̶ ̶i̶n̶t̶o̶ ̶t̶h̶i̶s̶ ̶s̶o̶l̶u̶t̶i̶o̶n̶ ̶a̶g̶a̶i̶n̶ ̶s̶o̶o̶n̶ ̶e̶n̶o̶u̶g̶h̶ ̶b̶u̶t̶ ̶f̶o̶r̶ ̶s̶a̶k̶e̶ ̶o̶f̶ ̶f̶a̶s̶t̶ ̶i̶t̶e̶r̶a̶t̶i̶o̶n̶ ̶o̶f̶ ̶o̶u̶r̶ ̶M̶V̶P̶,̶ ̶I̶ ̶w̶a̶n̶t̶e̶d̶ ̶t̶o̶ ̶r̶e̶s̶e̶a̶r̶c̶h̶ ̶a̶l̶t̶e̶r̶n̶a̶t̶i̶v̶e̶ ̶s̶o̶l̶u̶t̶i̶o̶n̶s̶.̶ ̶I̶f̶ ̶y̶o̶u̶ ̶a̶r̶e̶n̶’̶t̶ ̶u̶s̶i̶n̶g̶ ̶w̶e̶b̶p̶a̶c̶k̶ ̶o̶r̶ ̶p̶r̶e̶f̶e̶r̶ ̶t̶o̶ ̶h̶a̶c̶k̶ ̶a̶w̶a̶y̶ ̶a̶t̶ ̶t̶h̶i̶s̶ ̶s̶o̶l̶u̶t̶i̶o̶n̶,̶ ̶I̶ ̶r̶e̶c̶o̶m̶m̶e̶n̶d̶ ̶g̶i̶v̶i̶n̶g̶ ̶i̶t̶ ̶a̶ ̶s̶h̶o̶t̶ ̶(̶a̶n̶d̶ ̶s̶h̶a̶r̶e̶ ̶y̶o̶u̶r̶ ̶f̶e̶e̶d̶b̶a̶c̶k̶ ̶i̶n̶ ̶t̶h̶e̶ ̶c̶o̶m̶m̶e̶n̶t̶s̶!̶)̶.̶
̶W̶r̶i̶t̶e̶r̶’̶s̶ ̶n̶o̶t̶e̶,̶ ̶J̶a̶n̶ ̶2̶0̶1̶8̶:̶ ̶W̶h̶e̶n̶ ̶I̶ ̶o̶r̶i̶g̶i̶n̶a̶l̶l̶y̶ ̶w̶r̶o̶t̶e̶ ̶t̶h̶i̶s̶,̶ ̶I̶ ̶c̶o̶u̶l̶d̶ ̶n̶o̶t̶ ̶g̶e̶t̶ ̶t̶h̶i̶s̶ ̶t̶o̶ ̶w̶o̶r̶k̶.̶ ̶B̶u̶t̶ ̶n̶o̶w̶ ̶I̶ ̶c̶a̶n̶ ̶o̶f̶ ̶c̶o̶u̶r̶s̶e̶,̶ ̶a̶s̶ ̶i̶t̶’̶s̶ ̶b̶e̶e̶n̶ ̶a̶ ̶f̶e̶w̶ ̶y̶e̶a̶r̶s̶ ̶o̶f̶ ̶J̶S̶ ̶e̶n̶g̶i̶n̶e̶e̶r̶i̶n̶g̶ ̶u̶n̶d̶e̶r̶ ̶m̶y̶ ̶b̶e̶l̶t̶,̶ ̶a̶s̶ ̶c̶a̶n̶ ̶b̶e̶ ̶s̶e̶e̶n̶ ̶b̶y̶ ̶a̶ ̶f̶e̶w̶ ̶o̶f̶ ̶m̶y̶ ̶c̶o̶m̶m̶i̶t̶s̶ ̶t̶o̶ ̶t̶h̶i̶s̶ ̶r̶e̶p̶o̶.̶ ̶T̶w̶o̶ ̶i̶m̶p̶o̶r̶t̶a̶n̶t̶ ̶t̶h̶i̶n̶g̶s̶ ̶t̶o̶ ̶n̶o̶t̶e̶:̶ ̶f̶i̶r̶s̶t̶,̶ ̶d̶o̶n̶’̶t̶ ̶u̶s̶e̶ ̶t̶h̶i̶s̶ ̶p̶a̶c̶k̶a̶g̶e̶ ̶u̶n̶l̶e̶s̶s̶ ̶y̶o̶u̶ ̶a̶b̶s̶o̶l̶u̶t̶e̶l̶y̶ ̶m̶u̶s̶t̶ ̶h̶a̶v̶e̶ ̶t̶o̶.̶ ̶T̶h̶a̶t̶ ̶i̶s̶ ̶a̶ ̶r̶e̶c̶o̶m̶m̶e̶n̶d̶a̶t̶i̶o̶n̶ ̶f̶r̶o̶m̶ ̶S̶e̶g̶m̶e̶n̶t̶.̶ ̶S̶e̶c̶o̶n̶d̶l̶y̶,̶ ̶t̶h̶i̶s̶ ̶s̶o̶l̶u̶t̶i̶o̶n̶ ̶w̶i̶l̶l̶ ̶o̶n̶l̶y̶ ̶a̶d̶d̶ ̶t̶h̶e̶ ̶a̶n̶a̶l̶y̶t̶i̶c̶s̶.̶j̶s̶ ̶s̶n̶i̶p̶p̶e̶t̶ ̶t̶o̶ ̶y̶o̶u̶r̶ ̶a̶p̶p̶.̶ ̶I̶t̶ ̶w̶i̶l̶l̶ ̶n̶o̶t̶ ̶h̶a̶n̶d̶l̶e̶ ̶t̶r̶a̶c̶k̶ ̶c̶a̶l̶l̶s̶.̶ ̶I̶f̶ ̶y̶o̶u̶ ̶d̶o̶ ̶d̶e̶c̶i̶d̶e̶ ̶t̶o̶ ̶u̶s̶e̶ ̶t̶h̶i̶s̶ ̶n̶p̶m̶ ̶p̶a̶c̶k̶a̶g̶e̶,̶ ̶t̶h̶e̶r̶e̶ ̶i̶s̶ ̶a̶n̶ ̶o̶p̶t̶i̶o̶n̶ ̶t̶o̶ ̶p̶r̶e̶v̶e̶n̶t̶ ̶t̶h̶e̶ ̶a̶n̶a̶l̶y̶t̶i̶c̶s̶.̶p̶a̶g̶e̶(̶)̶ ̶m̶e̶t̶h̶o̶d̶ ̶f̶r̶o̶m̶ ̶f̶i̶r̶i̶n̶g̶,̶ ̶t̶h̶e̶r̶e̶b̶y̶ ̶a̶l̶l̶o̶w̶i̶n̶g̶ ̶y̶o̶u̶ ̶t̶o̶ ̶i̶n̶v̶o̶k̶e̶ ̶p̶a̶g̶e̶ ̶c̶a̶l̶l̶s̶ ̶m̶a̶n̶u̶a̶l̶l̶y̶.̶ ̶I̶ ̶h̶i̶g̶h̶l̶y̶ ̶s̶u̶g̶g̶e̶s̶t̶ ̶p̶u̶t̶t̶i̶n̶g̶ ̶f̶a̶l̶s̶e̶ ̶f̶o̶r̶ ̶t̶h̶i̶s̶ ̶o̶p̶t̶i̶o̶n̶ ̶s̶o̶ ̶y̶o̶u̶ ̶c̶a̶n̶ ̶h̶a̶v̶e̶ ̶b̶e̶t̶t̶e̶r̶ ̶c̶o̶n̶t̶r̶o̶l̶ ̶o̶f̶ ̶w̶h̶e̶n̶ ̶p̶a̶g̶e̶ ̶c̶a̶l̶l̶s̶ ̶g̶e̶t̶ ̶i̶n̶v̶o̶k̶e̶d̶.̶

Second Solution: Attach the code to React’s onEnter hook

Another solution I tried was to invoke the analytics.page() method within React’s router. React has an onEnter hook that allows us to invoke methods within the routing itself.

This seemed like a promising idea. I especially like it because it does the best job of separating concerns. It’s also DRY’er and more modular than the next solution.

I was about to try it, and definitely encourage you to give it a shot (and update me via twitter or in the comments below) but I luckily heard back from the Segment Success Team. Their awesome team member, William, gave me the following solution.

Third and final solution: Use React’s component lifecycle flow

Occam’s Razor. Of course! The easiest solution to a problem is usually the best. Why over think it?

Among competing hypotheses, the one with the fewest assumptions should be selected.

What I did, based on William’s feedback, was place the core snippet in the head tag of my index.html file and then place all the important methods — page, identify, track — in React’s various lifecycle components, which was usually the componentDidMount.

For those unfamiliar with React’s component lifecycle, I highly suggest reading this article or just understand that React has a handful of methods that get invoked at specific times.

For example, for our pageview data, we most likely want to invoke the analytics.page() call immediately after a page has rendered. The best lifecycle method to use for the page call would be componentDidMount. As mentioned in the article above, “The DOM can be accessed in this method… Any DOM interactions should always happen in this phase not inside the render method.

For analytics.identify(), the best time to load it is on the login or signup page and within React’s componentDidUpdate method. This method is used to perform DOM operations after the data has been updated, i.e. when the user has successfully logged in or signed up.

For tracking click events, a good time to fire the analytics.track() method would be in our handleSubmit method since this gets fired once the form has been submitted.

All Done.

Look at all that beautiful data showing up in our Segment debugger!

And that’s it! Easy, right? Was this article helpful? If so, please like the article below. Or leave a comment if you have any questions.

0 0 vote
Article Rating

Write a Comment

Comment

Subscribe
Notify of
guest
0 Comments
Inline Feedbacks
View all comments