It is easy to start tracking your Single Page Application (SPA) or Progressive Web App (PWA) using Matomo Analytics. The easiest way to do this is using the Matomo Tag Manager (learn more using the steps below, alternatively you can use the JavaScript Tracking code (following this guide).
If you're using Tag Manager to implement your Matomo Analytics Tracking, then in your Single Page Application, whenever there is a new page loaded in your app you will need your Matomo Tag to be triggered for the Page view to be tracked.
To trigger your Matomo tag (which calls trackPageView
), you can either:
{{PageTitle}}
.{{PageOrigin}}/{{PageHash}}
if your SPA or PWA has a #
in the URL to navigate to different pages. Otherwise, set it as {{PageUrl}}
{event: 'mtm.PageView'}
by calling the following line in JavaScript: window._mtm.push({'event': 'mtm.PageView'});
. This would also work similarly when you use the 'DOM Ready Trigger' (call window._mtm.push({'event': 'DOMReady'});
) or when you use the 'Window Loaded Trigger' (call _mtm.push({'event': 'WindowLoad'});
.If you're not using the Tag Manager, then you need to embed your Matomo JavaScript tracking code into your single-page website or web application as you would for a classic website.
To embed the tracking code manually, you have two options:
In the sections below you will learn how to setup Matomo tracking with single-page applications (SPAs). It addresses how to track page views, reset custom variables and dimensions, update referrer URLs, scan for new content, and implement Heatmap & Session Recording. A comprehensive example illustrates how these elements integrate within an SPA environment. Information on offline tracking and support resources are also provided.
The challenge begins when you need to track a new page view. A single-page application is different from a usual website as there is no regular new page load and Matomo cannot detect automatically when a new page is viewed. This means you need to let Matomo know whenever the URL and the page title changes. You can do
this using the methods setCustomUrl
and setDocumentTitle
like this:
window.addEventListener('hashchange', function() {
_paq.push(['setCustomUrl', '/' + window.location.hash.substr(1)]);
_paq.push(['setDocumentTitle', 'My New Title']);
_paq.push(['trackPageView']);
});
If you have set any Custom Variables in scope “page”, you need to make sure to delete these custom variables again as they would be attributed to the new page view as well otherwise:
_paq.push(['deleteCustomVariables', 'page']);
_paq.push(['trackPageView']);
Note: we recommend you use Custom Dimensions instead of Custom Variables as they will be deprecated in the future.
Similar to Custom Variables, you also need to unset Custom Dimensions when changing the page as they would otherwise be tracked again.
_paq.push(['deleteCustomDimension', 1]);
_paq.push(['trackPageView']);
Depending on whether you want to track the previous page as a referrer for the new page view, you should update the referrer URL by setting it to the previous page URL:
_paq.push(['setReferrerUrl', previousPageUrl]);
_paq.push(['trackPageView']);
When you show a new page, your single-page DOM might change as well. For example, you might replace parts of your page with new content that you loaded from your server via Ajax. This means you need to instruct Matomo to scan the DOM for new content. We'll now go over various content types (Videos & Audio, Forms, Links and Downloads, Content tracking).
If you use the Media Analytics feature to track your videos and audios, whenever a new page is displayed you need to call the following method:
_paq.push(['MediaAnalytics::scanForMedia', documentOrElement]);
When you don’t pass any parameter, it will scan the entire DOM for new media. Alternatively, you can pass an element to scan only a certain area of your website or app for new media.
If you use the Form Analytics feature to measure the performance of your online forms, whenever a new page is displayed you need to call the following method:
_paq.push(['FormAnalytics::scanForForms', documentOrElement]);
Where documentOrElement
points either to document
to re-scan the entire
DOM (the default when no parameter is set) or you can pass an element to
restrict the re-scan to a specific area.
If you use the A/B Testing feature to test your experiments, whenever a new page is displayed you need to embed the js code again before tracking a new pageview as explained below:
window.addEventListener('pathchange', function() {
var _paq = window._paq = window._paq || [];
_paq.push(['setCustomUrl', window.location.pathname]);
_paq.push(['setDocumentTitle', document.title]);
_paq.push(['AbTesting::create', {
name: 'theExperimentName',
includedTargets: [{"attribute":"url","type":"starts_with","value":"http:\/\/www.example.org","inverted":"0"}],
excludedTargets: [],
variations: [
{
name: 'original',
activate: function (event) {
// usually nothing needs to be done here
}
},
{
name: 'blue',
activate: function(event) {
// eg $('#btn').attr('style', 'color: ' + this.name + ';');
}
}
]
}]);
_paq.push(['trackPageView']);
});
Supposing that you use the link tracking feature to measure outlinks and downloads, Matomo needs to re-scan the entire DOM for newly added links whenever your DOM changes. To make sure Matomo will track such links, call this method:
_paq.push(['enableLinkTracking']);
If you use the Content Tracking feature, whenever a new page is displayed and some parts of your DOM changes, you need to call this method :
_paq.push(['trackContentImpressionsWithinNode', documentOrElement]);
Where documentOrElement
points either to document
or an element similar to the other methods. Matomo will then scan the page for newly added content blocks.
To support single-page websites and web applications out of the box, Heatmap & Session Recording will automatically detect a new page view when you call the trackPageView
method. This applies if you call trackPageView
several times without an actual page reload. Matomo will stop the recording of any activities after each call of trackPageView
and re-evaluate whether it should record activities for the new page based on the new URL.
If you have a single-page website and you use trackPageView
for any other purposes than an actual page view, it is recommended to disable the default behaviour using this method and let Heatmap & Session Recording explicitly know when there is a new page view by calling the two methods disableAutoDetectNewPageView
and setNewPageView
.
If you're setting a Custom URL in the single-page website, you may need to use the matchTrackerUrl()
in order to allow the Matomo tracker to correctly trigger Heatmaps and Session Recordings.
Learn more in the JS Tracker API reference for Heatmaps & Session Recording.
In this example we show how everything works together assuming you want to track a new page whenever a hash changes:
var currentUrl = location.href;
window.addEventListener('hashchange', function() {
_paq.push(['setReferrerUrl', currentUrl]);
currentUrl = '/' + window.location.hash.substr(1);
_paq.push(['setCustomUrl', currentUrl]);
_paq.push(['setDocumentTitle', 'My New Title']);
// remove all previously assigned custom variables, requires Matomo (formerly Piwik) 3.0.2
_paq.push(['deleteCustomVariables', 'page']);
_paq.push(['AbTesting::create', {
name: 'theExperimentName',
includedTargets: [{"attribute":"url","type":"starts_with","value":"http:\/\/www.example.org","inverted":"0"}],
excludedTargets: [],
variations: [
{
name: 'original',
activate: function (event) {
// usually nothing needs to be done here
}
},
{
name: 'blue',
activate: function(event) {
// eg $('#btn').attr('style', 'color: ' + this.name + ';');
}
}
]
}]);
_paq.push(['trackPageView']);
// make Matomo aware of newly added content
var content = document.getElementById('content');
_paq.push(['MediaAnalytics::scanForMedia', content]);
_paq.push(['FormAnalytics::scanForForms', content]);
_paq.push(['trackContentImpressionsWithinNode', content]);
_paq.push(['enableLinkTracking']);
});
If your web application requires offline tracking, please refer to the Matomo offline tracking guide.
If you have any questions or need help, please get in touch with our support team or for free support: ask on the forum.