75

I am trying to add Google Analytics to a React Web Application.

I know how to do it in HTML/CSS/JS sites and I have integrated it in an AngularJS app too. But, I'm not quite sure how to go about it when it comes to react.

With HTML/CSS/JS, I had just added it to every single page.

What I had done with AngularJS was adding GTM and GA script to index.html and added UA-labels to the HTML divs (and buttons) to get clicks.

How can I do that with React?

Please help!

Raj
  • 2,888
  • 3
  • 25
  • 46
  • Note that `react-ga` only works with Universal Analytics, whereas new Google Analytics properties are GA4 properties by default. https://stackoverflow.com/q/64623059/9154668 – Andrew Oct 18 '21 at 17:59

7 Answers7

112

Update: Feb 2019
As I saw that this question is being searched a lot, I decided to expand my explanation.
To add Google Analytics to React, I recommend using React-GA.
Add by running:
npm install react-ga --save

Initialization:
In a root component, initialize by running:

import ReactGA from 'react-ga';
ReactGA.initialize('Your Unique ID');

To report page view:

ReactGA.pageview(window.location.pathname + window.location.search);

To report custom event:

ReactGA.event({
  category: 'User',
  action: 'Sent message'
});

More instructions can be found in the github repo


The best practice for this IMO is using react-ga. Have a look at the github rep

Matan Bobi
  • 2,565
  • 1
  • 14
  • 26
  • I agree, I'm using it with react-ga package. Note that you should use ReactGA.set method to trigger following pageviews. – Sebastijan Dumančić Mar 14 '18 at 14:35
  • Yeah, I checked out react-ga before posting this. Is there no other way? Also, how would I get clicks? – Raj Mar 14 '18 at 15:22
  • 1
    You can get them by using event. Have a look here: https://github.com/react-ga/react-ga#reactgaeventargs – Matan Bobi Mar 14 '18 at 15:29
  • Ended up using react-ga and it works great. Thanks a lot. :) – Raj Mar 20 '18 at 09:14
  • Is there any way to use ReactGA.initiliaze(trackid) by fetching the trackid from a response. I use a function for returning ReactGA.initiliaze(trackid) when trackid is defined but i get an error that cannot read property parentNode of udefined – user7334203 Jun 29 '18 at 07:06
  • why not just call `ReactGA.initialize(trackid)` once you have an id? after you receive the id from the server – Matan Bobi Jul 03 '18 at 13:16
  • Can we use ReactGA on multiple pages and track page views? – Reema Parakh Dec 03 '18 at 07:34
  • 2
    @ReemaParakh Sure. Just call `ReactGA.pageview('your/page');` – Matan Bobi Dec 03 '18 at 08:47
  • 3
    If you use `react-router` It seems easiest to me to put this integration in where you set up your routes: ` { ReactGA.pageview(props.location.pathname); return ; }} />` – alaiacano May 30 '19 at 11:38
  • @alaiacano, You can definitely do that, just don't forget to pass the props to your component.. – Matan Bobi Jun 03 '19 at 16:26
  • where in the root component? – s2t2 Oct 12 '20 at 03:58
  • @s2t2 what do you mean? can you elaborate? – Matan Bobi Oct 12 '20 at 06:21
  • 1
    @MatanBobi nevermind, I figured it out: init at the top (above component class definition), and you log page views from componentDidMount – s2t2 Oct 12 '20 at 20:42
  • Why not just add an HTML snippet provided by Google Analytics to the index.html file ? – Prateek Goyal Apr 02 '21 at 07:07
  • 1
    @Prateek, you can do it, as other answers suggest.. But I believe in encapsulating this and just getting the functionality. – Matan Bobi Apr 04 '21 at 06:52
  • To report page views do you need to add ```ReactGA.pageview(window.location.pathname + window.location.search);``` to each page or just at the root? – SamHeyman Aug 25 '21 at 08:47
  • @SamHeyman you'll need to do that whenever a route changes so you can either do that on the `useEffect` after the first mount of a page component or catch a route change in a generic function. Whatever you choose :) – Matan Bobi Aug 26 '21 at 12:13
25

If you prefer not to use a package this is how it can work in a react application. Add the "gtag" in index.html

<!-- index.html -->

<script>
            window.dataLayer = window.dataLayer || [];
            function gtag() {
                dataLayer.push(arguments);
            }
            gtag("js", new Date());

            gtag("config", "<GA-PROPERTYID>");
        </script>

In the submit action of the login form, fire off the event

window.gtag("event", "login", {
            event_category: "access",
            event_label: "login"
        });
Giwan
  • 1,434
  • 14
  • 15
23

Without using a package this is how I would do it:

In your index.js (in the render method):

    {/* Global site tag (gtag.js) - Google Analytics */}
    <script
      async
      src="https://www.googletagmanager.com/gtag/js?id=YOUR_TRACKING_ID"
    />
    <script>{injectGA()}</script>

And outside the class:

const injectGA = () => {
  if (typeof window == 'undefined') {
    return;
  }
  window.dataLayer = window.dataLayer || [];
  function gtag() {
    window.dataLayer.push(arguments);
  }
  gtag('js', new Date());

  gtag('config', 'YOUR_TRACKING_ID');
};
cameck
  • 1,880
  • 20
  • 30
3

One other great library that you can check is redux-beacon.

It gets integrated very easily with react/redux application and has a great documentation for it. ReactGA is good too but with redux-beacon, you won't clutter your app code with google analytics code as it works via its own middleware.

Jim G.
  • 14,600
  • 19
  • 100
  • 158
utkarsh
  • 133
  • 4
  • 1
    This is the better solution *if* you're using `redux` in the application (which you probably should be doing). It's better to treat your analytics as an externality, rather than integral to your code. `redux-beacon` allows you to configure connections to _multiple_ analytics services based on redux actions, avoiding coupling your code to your analytics provider. – Brendan Moore Jul 18 '18 at 14:22
2

Escape the analytics code with dangerouslySetInnerHTML

First you have of course to share the header code to all pages, e.g. as asked at: React js do common header

Then, this Next.js answer https://stackoverflow.com/a/24588369/895245 gives a good working code that should also work outside of Next.js. It escapes the analytics code with dangerouslySetInnerHTML:

  <script async src="https://www.googletagmanager.com/gtag/js?id=UA-47867706-3"></script>
  <script
    dangerouslySetInnerHTML={{
      __html: `window.dataLayer = window.dataLayer || [];
function gtag(){dataLayer.push(arguments);}
gtag('js', new Date());
gtag('config', 'UA-47867706-3', { page_path: window.location.pathname });
`,
    }}
  />

where you should replace UA-47867706-3 with your own code.

This code is exactly the code that Google gives, but with the following modification: we added the:

{ page_path: window.location.pathname }

to gtag('config' for it to be able to get the visited path, since this is a JavaScript SPA.

This generates the desired output on the browser:

<script async="" src="https://www.googletagmanager.com/gtag/js?id=UA-47867706-3"></script><script>window.dataLayer = window.dataLayer || [];
function gtag(){dataLayer.push(arguments);}
gtag('js', new Date());
gtag('config', 'UA-47867706-3', { page_path: window.location.pathname });
</script>

The only other divergence from the exact code given by Google is the async="" vs async, but both of those are equivalent in HTML since it is a boolean attribute, see also: What's the proper value for a checked attribute of an HTML checkbox?

Escaping with dangerouslySetInnerHTML is necessary because otherwise React interprets the code inside script a JSX and that fails with:

Syntax error: Unexpected token, expected "}"

  21 |           <script>
  22 |             window.dataLayer = window.dataLayer || [];
> 23 |             function gtag(){dataLayer.push(arguments);}
     |                                                      ^
  24 |             gtag('js', new Date());
  25 |
  26 |             gtag('config', 'UA-47867706-3');

I wish they would just automatically escape stuff inside script for us.

Finally to get page switches, you also have to track that with more code, see the Next.js answer mentioned above for an example.

Related: Adding script tag to React/JSX

Tested on react 17.0.2, next.js 10.2.2.

  • Do not follow such approaches `dangerouslySetInnerHTML` is React’s replacement for using innerHTML in the browser DOM but setting HTML from code is risky because it’s easy to inadvertently expose your users to a cross-site scripting (XSS) attack. – NevetsKuro Feb 05 '22 at 03:53
  • 1
    @NevetsKuro there's no risk when injecting a string literal like this, only if you have user input variables. – Ciro Santilli Путлер Капут 六四事 Feb 05 '22 at 07:24
  • Yes my bad. It is for the case for user input only. But you need to mention that in this solution, as React keeps popping this as a bug/warning. Ref: https://pragmaticwebsecurity.com/articles/spasecurity/react-xss-part2.html – NevetsKuro Feb 07 '22 at 06:41
0

I suggest embedding the Segment script into your index.html, use the analytics library that is accessible on the window object, and add tracking calls onto React’s event handlers:

export default class SignupButton extends Component {
  trackEvent() {
    window.analytics.track('User Signup');
  }

  render() {
    return (
      <button onClick={this.trackEvent}>
        Signup with Segment today!
      </button>
    );
  }
}

I’m the maintainer of https://github.com/segmentio/analytics-react. I recommend checking it out if you want to solve this problem by using one singular API to manage your customer data, and be able to integrate into any other analytics tool (we support over 250+ destinations) without writing any additional code.

William
  • 61
  • 3
  • Looks like this library is deprecated and no longer supported. This must be the replacement: https://github.com/segmentio/analytics.js. – Ruslan Kazakov Oct 11 '20 at 23:56
0

Looking at google's site https://developers.google.com/analytics/devguides/collection/analyticsjs,

you could also add Google Analytics using this function:

const enableGA = () => {
  !function(A,n,g,u,l,a,r){A.GoogleAnalyticsObject=l,A[l]=A[l]||function(){
  (A[l].q=A[l].q||[]).push(arguments)},A[l].l=+new Date,a=n.createElement(g),
  r=n.getElementsByTagName(g)[0],a.src=u,r.parentNode.insertBefore(a,r)
  }(window,document,'script','https://www.google-analytics.com/analytics.js','ga');

  ga('create', 'UA-XXXXXXXX-X');
  ga('send', 'pageview');
}

This way you don't need an external library, and it's pretty quick to setup.

e_netr
  • 493
  • 1
  • 5
  • 21