How to set up Google Analytics for React-Router?

Google AnalyticsReactjsReact Router

Google Analytics Problem Overview


I'm trying set up Google Analytics on my react site, and have come across a few packages, but none of which has the kind of set up that I have in terms of examples. Was hoping someone could shed some light on this.

The package I'm looking at is, react-ga.

My render method on my index.js looks like this.

React.render((
<Router history={createBrowserHistory()}>
    <Route path="/" component={App}>
        <IndexRoute component={Home} onLeave={closeHeader}/>
        <Route path="/about" component={About} onLeave={closeHeader}/>
        <Route path="/gallery" component={Gallery} onLeave={closeHeader}/>
        <Route path="/contact-us" component={Contact} onLeave={closeHeader}>
            <Route path="/contact-us/:service" component={Contact} onLeave={closeHeader}/>
        </Route>
        <Route path="/privacy-policy" component={PrivacyPolicy} onLeave={closeHeader} />
        <Route path="/feedback" component={Feedback} onLeave={closeHeader} />
    </Route>
    <Route path="*" component={NoMatch} onLeave={closeHeader}/>
</Router>), document.getElementById('root'));

Google Analytics Solutions


Solution 1 - Google Analytics

Keep a reference to your history object. i.e.

import { createBrowserHistory } from 'history';

var history = createBrowserHistory();

ReactDOM.render((
    <Router history={history}>
        [...]

Then add a listener to record each pageview. (This assumes you've already set up the window.ga object in the usual manner.)

history.listen((location) => {
    window.ga('set', 'page', location.pathname + location.search);
    window.ga('send', 'pageview');
});

Solution 2 - Google Analytics

Since react-router v5.1.0 this can be solved a lot easier with useLocation.

usePageTracking.js

import { useEffect} from "react";
import { useLocation } from "react-router-dom";
import ReactGA from "react-ga";

const usePageTracking = () => {
  const location = useLocation();

  useEffect(() => {
    ReactGA.initialize("UA-000000000-0");
    ReactGA.pageview(location.pathname + location.search);
  }, [location]);
};

export default usePageTracking;

App.js

const App = () => {
  usePageTracking();

  return (...);
};

See also:

Here's a bit smarter version:

usePageTracking.js

import { useEffect, useState } from "react";
import { useLocation } from "react-router-dom";
import ReactGA from "react-ga";

const usePageTracking = () => {
  const location = useLocation();
  const [initialized, setInitialized] = useState(false);

  useEffect(() => {
    if (!window.location.href.includes("localhost")) {
      ReactGA.initialize("UA-000000000-0");
    }
    setInitialized(true);
  }, []);

  useEffect(() => {
    if (initialized) {
      ReactGA.pageview(location.pathname + location.search);
    }
  }, [initialized, location]);
};

export default usePageTracking;

Solution 3 - Google Analytics

Given that google analytics is loaded and initialised with a tracking id.

Here is a solution for react-router version 4 using the <Route> component to track page views.

<Route path="/" render={({location}) => {
  if (typeof window.ga === 'function') {
    window.ga('set', 'page', location.pathname + location.search);
    window.ga('send', 'pageview');
  }
  return null;
}} />

You simply render this component inside the <Router> (but not as a direct child of a <Switch>).

What happens is that whenever the location prop changes it causes a re-render of this component (not actually rendering anything) that fire a pageview.

Solution 4 - Google Analytics

I'm using React Router v4 and the Google Analytics Global Site Tag, which appears to be recommended at the time of writing this.

And here's my solution:

Create a component wrapped in withRouter from react-router-dom:

import React from 'react';
import { withRouter } from 'react-router-dom';
import { GA_TRACKING_ID } from '../config';

class GoogleAnalytics extends React.Component {
    componentWillUpdate ({ location, history }) {
        const gtag = window.gtag;

        if (location.pathname === this.props.location.pathname) {
            // don't log identical link clicks (nav links likely)
            return;
        }

        if (history.action === 'PUSH' &&
            typeof(gtag) === 'function') {
            gtag('config', GA_TRACKING_ID, {
                'page_title': document.title,
                'page_location': window.location.href,
                'page_path': location.pathname
            });
        }
    }

    render () {
        return null;
    }
}

export default withRouter(GoogleAnalytics);

Simply add the component within your router (I believe ideally after any routes that would be matched and any Switch components, because the analytics function should not be priority over your site rendering):

import React from 'react';
import { BrowserRouter as Router, Route, Switch } from 'react-router-dom';
import IndexPage from './IndexPage';
import NotFoundPage from './NotFoundPage';
import GoogleAnalytics from './GoogleAnalytics';

const App = () => (
	<Router>
		<Switch>
			<Route exact path="/" component={IndexPage} />
			<Route component={NotFoundPage} />
		</Switch>
		<GoogleAnalytics />
	</Router>
);

As stated:

> withRouter will re-render its component every time the route changes with the > same props as render props

So when the route changes, the GoogleAnalytics component will update, it will receive the new location as props, and history.action will be either PUSH for a new history item or POP to signal going backwards through the history (which I think shouldn't trigger a page view, but you can adjust the if statements in componentWillUpdate as you see fit (you could even try componentDidUpdate with this.props instead, but I'm unsure which is better)).

Solution 5 - Google Analytics

Note if you're using the react-router-dom package from react-router-4 you can handle this like so:

import { Router, Route } from 'react-router-dom';
import { createBrowserHistory } from 'history';

const history = createBrowserHistory();
const initGA = (history) => {
  (function(i,s,o,g,r,a,m){i['GoogleAnalyticsObject']=r;i[r]=i[r]||function(){
  (i[r].q=i[r].q||[]).push(arguments)},i[r].l=1*new Date();a=s.createElement(o),
  m=s.getElementsByTagName(o)[0];a.async=1;a.src=g;m.parentNode.insertBefore(a,m)
  })(window,document,'script','https://www.google-analytics.com/analytics.js','ga');

  ga('create', 'YOUR_IDENTIFIER_HERE', 'auto');
  ga('send', 'pageview');

  history.listen((location) => {
    console.log("tracking page view: " + location.pathname);
    ga('send', 'pageview', location.pathname);
  });
};

initGA(history);

class App extends Component { //eslint-disable-line
  render() {
    return
      (<Router history={history} >
         <Route exact path="/x" component={x} />
         <Route exact path="/y" component={y} />
       </Router>)
  }
}

Note that this requires you to install the history package (npm install history). This is already a dependency of react-router-dom so you're not adding any page weight here.

Also note: It is not possible to use the BrowserRouter component AND instrument your ga tracking this way. This is okay because the BrowserRouter component is just a really thin wrapper around the Router object. We recreate the BrowserRouter functionality here with <Router history={history}> where const history = createBrowserHistory();.

Solution 6 - Google Analytics

I would suggest using the excellent react-router-ga package that is extremely lightweight and easy to configure, especially when using the BrowserRouter wrapper.

Import the component:

import Analytics from 'react-router-ga';

Then simply add the <Analytics> within your BrowserRouter:

<BrowserRouter>
    <Analytics id="UA-ANALYTICS-1">
        <Switch>
            <Route path="/somewhere" component={SomeComponent}/>
        </Switch>
    </Analytics>
</BrowserRouter>

Solution 7 - Google Analytics

I like how Mark Thomas Müller suggests here:

In your index.js

import ReactGA from 'react-ga'

ReactGA.initialize('YourAnalyticsID')

ReactDOM.render(<App />, document.getElementById('root'))

Where your routes are:

import React, { Component } from 'react'
import { Router, Route } from 'react-router-dom'
import createHistory from 'history/createBrowserHistory'
import ReactGA from 'react-ga'

const history = createHistory()
history.listen(location => {
	ReactGA.set({ page: location.pathname })
	ReactGA.pageview(location.pathname)
})

export default class AppRoutes extends Component {
	componentDidMount() {
		ReactGA.pageview(window.location.pathname)
	}

	render() {
		return (
			<Router history={history}>
				<div>
					<Route path="/your" component={Your} />
					<Route path="/pages" component={Pages} />
					<Route path="/here" component={Here} />
				</div>
			</Router>
		)
	}
}

Short, scalable and simple :)

Solution 8 - Google Analytics

> Always go with the library's recommended way

In the React-GA documentation, they have added a community component recommended for using with React Router: https://github.com/react-ga/react-ga/wiki/React-Router-v4-withTracker

Implementation

import withTracker from './withTracker';

ReactDOM.render(
  <Provider store={store}>
    <ConnectedRouter history={history}>
      <Route component={withTracker(App, { /* additional attributes */ } )} />
    </ConnectedRouter>
  </Provider>,
  document.getElementById('root'),
);

Code

import React, { Component, } from "react";
import GoogleAnalytics from "react-ga";

GoogleAnalytics.initialize("UA-0000000-0");

const withTracker = (WrappedComponent, options = {}) => {
  const trackPage = page => {
    GoogleAnalytics.set({
      page,
      ...options,
    });
    GoogleAnalytics.pageview(page);
  };

  // eslint-disable-next-line
  const HOC = class extends Component {
    componentDidMount() {
      // eslint-disable-next-line
      const page = this.props.location.pathname + this.props.location.search;
      trackPage(page);
    }

    componentDidUpdate(prevProps) {
      const currentPage =
        prevProps.location.pathname + prevProps.location.search;
      const nextPage =
        this.props.location.pathname + this.props.location.search;

      if (currentPage !== nextPage) {
        trackPage(nextPage);
      }
    }

    render() {
      return <WrappedComponent {...this.props} />;
    }
  };

  return HOC;
};

export default withTracker;

Solution 9 - Google Analytics

here is a simplest way to track all paths with some work arounds:

npm i --save history react-ga

create a file history.js

import { createBrowserHistory } from "history"
import ReactGA from "react-ga"

ReactGA.initialize(process.env.REACT_APP_GA)

const history = createBrowserHistory()
history.listen((location) => {
    ReactGA.pageview(location.pathname)
})

// workaround for initial visit
if (window.performance && (performance.navigation.type === performance.navigation.TYPE_NAVIGATE)) {
    ReactGA.pageview("/")
}

export default history

and then import it to where is set your Router

import history from "./history"

...

class Route extends Component {
render() {
    return (
        <Router history={history}>
            <Switch>
              <Route path="/" exact component={HomePage} />
              ...
            </Switch>
        </Router>
    )
}

export default Route

> References:

> Gustavo Gonzalez | medium.com

> History | GitHub

Solution 10 - Google Analytics

First, in your index.js set onUpdate function to call ga

import ga from 'ga.js';
onUpdate() {
  console.log('=====GA=====>', location.pathname);
  console.log('=====GA_TRACKING_CODE=====>', GA_TRACKING_CODE);
  ga("send", "pageview", location.pathname);
}

render() {
  return (
    <Router onUpdate={this.onUpdate.bind(this)}>...</Router>
  );
}

And ga.js:

'use strict';
if(typeof window !== 'undefined' && typeof GA_TRACKING_CODE !== 'undefined') {
  (function(window, document, script, url, r, tag, firstScriptTag) {
    window['GoogleAnalyticsObject']=r;
    window[r] = window[r] || function() {
      (window[r].q = window[r].q || []).push(arguments)
    };
    window[r].l = 1*new Date();
    tag = document.createElement(script),
    firstScriptTag = document.getElementsByTagName(script)[0];
    tag.async = 1;
    tag.src = url;
    firstScriptTag.parentNode.insertBefore(tag, firstScriptTag);
  })(
    window,
    document,
    'script',
    '//www.google-analytics.com/analytics.js',
    'ga'
  );

  var ga = window.ga;

  ga('create', GA_TRACKING_CODE, 'auto');

  module.exports = function() {
    return window.ga.apply(window.ga, arguments);
  };
} else {
  module.exports = function() {console.log(arguments)};
}

Solution 11 - Google Analytics

I suggest using the Segment analytics library and following the React quickstart guide to track page calls using the react-router library. You can allow the <Route /> component to handle when the page renders and use componentDidMount to invoke page calls. The example below shows one way you could do this:

    const App = () => (
      <div>
        <Switch>
          <Route exact path="/" component={Home} />
          <Route path="/about" component={About} />
        </Switch>
      </div>
    );
    
    export default App;
    export default class Home extends Component {
      componentDidMount() {
        window.analytics.page('Home');
      }
    
      render() {
        return (
          <h1>
            Home page.
          </h1>
        );
      }
    }

I’m the maintainer of https://github.com/segmentio/analytics-react. With Segment, you’ll be able to switch different destinations on-and-off by the flip of a switch if you are interested in trying multiple analytics tools (we support over 250+ destinations) without having to write any additional code. 

Solution 12 - Google Analytics

If you use hash or browser history you can do:

import trackingHit from 'tracking';

import { Router, browserHistory } from 'react-router';
browserHistory.listen(trackingHit);
// OR
import { Router, hashHistory } from 'react-router';
hashHistory.listen(trackingHit);

where ./tracking.es6

export default function(location) {
 	console.log('New page hit', location.pathname);
    // Do your shizzle here
}

Solution 13 - Google Analytics

basic react-ga implementation with your index.js

var ReactGA = require('react-ga'); // require the react-ga module
ReactGA.initialize('Your-UA-ID-HERE'); // add your UA code 

function logPageView() { // add this function to your component
  ReactGA.set({ page: window.location.pathname + window.location.search });
  ReactGA.pageview(window.location.pathname + window.location.search);
}

React.render((
<Router history={createBrowserHistory()} onUpdate={logPageView} > // insert onUpdate props here
    <Route path="/" component={App}>
        <IndexRoute component={Home} onLeave={closeHeader}/>
        <Route path="/about" component={About} onLeave={closeHeader}/>
        <Route path="/gallery" component={Gallery} onLeave={closeHeader}/>
        <Route path="/contact-us" component={Contact} onLeave={closeHeader}>
            <Route path="/contact-us/:service" component={Contact} onLeave={closeHeader}/>
        </Route>
        <Route path="/privacy-policy" component={PrivacyPolicy} onLeave={closeHeader} />
        <Route path="/feedback" component={Feedback} onLeave={closeHeader} />
    </Route>
    <Route path="*" component={NoMatch} onLeave={closeHeader} />
</Router>), document.getElementById('root'));

Solution 14 - Google Analytics

Based on @david-l-walsh and @bozdoz suggestions

I created a HOC that execute the window.ga('set','page','{currentUrl}) and window.ga('send', 'pageview'); function and is easly used directly in the router page...

this is the HOC:

import React from 'react';
import { history } from '../../store'; // or wherever you createBrowserHistory(); invokation is

function withGAHistoryTrack(WrappedComponent) {
  return class extends React.Component {
    constructor(props) {
      super(props);
    }

    componentDidMount() {
      const { location } = history;
      const page = location.pathname + location.search;

      if (typeof window.ga === 'function') {
        window.ga('set', 'page', page);
        window.ga('send', 'pageview');
      }
    }

    render() {
      return <WrappedComponent {...this.props} />;
    }
  };
}

export default withGAHistoryTrack;

and is used this way in the router page:

<Route
 path={'yourPath'}
 component={withGAHistoryTrack(yourComponent)}
 exact
/>

Solution 15 - Google Analytics

For dynamically updating url on some event (like onClick etc), following can be used:

 //Imports
 import ReactGA from "react-ga";
 import { createBrowserHistory } from "history";

 // Add following on some event, like onClick (depends on your requirement)
 const history = createBrowserHistory();
 ReactGA.initialize("<Your-UA-ID-HERE>");
 ReactGA.pageview(history.location.pathname);

Attributions

All content for this solution is sourced from the original question on Stackoverflow.

The content on this page is licensed under the Attribution-ShareAlike 4.0 International (CC BY-SA 4.0) license.

Content TypeOriginal AuthorOriginal Content on Stackoverflow
QuestionJohn FuView Question on Stackoverflow
Solution 1 - Google AnalyticsDavid L. WalshView Answer on Stackoverflow
Solution 2 - Google AnalyticsthisismydesignView Answer on Stackoverflow
Solution 3 - Google AnalyticsheyhugoView Answer on Stackoverflow
Solution 4 - Google AnalyticsbozdozView Answer on Stackoverflow
Solution 5 - Google AnalyticsPeter BergView Answer on Stackoverflow
Solution 6 - Google AnalyticsTosheView Answer on Stackoverflow
Solution 7 - Google AnalyticsJöckerView Answer on Stackoverflow
Solution 8 - Google AnalyticsParasView Answer on Stackoverflow
Solution 9 - Google AnalyticsfsilvaView Answer on Stackoverflow
Solution 10 - Google AnalyticsWeiYuanView Answer on Stackoverflow
Solution 11 - Google AnalyticsWilliamView Answer on Stackoverflow
Solution 12 - Google AnalyticssidonaldsonView Answer on Stackoverflow
Solution 13 - Google AnalyticsIsaac PakView Answer on Stackoverflow
Solution 14 - Google AnalyticsYuri ScarbaciView Answer on Stackoverflow
Solution 15 - Google AnalyticsSantosh PillaiView Answer on Stackoverflow