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 ho
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
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.
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)).
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
}
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();
.
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');
});