Matomo (fka. Piwik) client for Angular applications
npm install ngx-matomo-client
ngx-matomo-client
**Note: this library was previously distributed as @ngx-matomo/tracker and @ngx-matomo/router packages. Since
version 5, it is now distributed as a single package ngx-matomo-client.
Follow instructions below for how to
easily migrate.**

Compatibility table:
| Matomo | Angular | ngx-matomo-client | @ngx-matomo/tracker
@ngx-matomo/router |
| --------- | ------- | ---------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------- |
| 3 or 4 | 9 to 12 | | 1.x (docs) |
| 3 or 4 | 13 | | 2.x (docs) |
| 3 or 4 | 14 | | 3.x (docs) |
| 3 or 4 | 15 | 5.x (docs) | 4.0.x (docs) |
| 3, 4 or 5 | 16 | 5.x (docs) | 4.x (docs) _(deprecated)_ |
| 3, 4 or 5 | 17 | 6.x | |
| 3, 4 or 5 | 18 | 6.2.x or above | |
| 3, 4 or 5 | 19 | 7.x | |
| 3, 4 or 5 | 20 | 8.x | |
| 3, 4 or 5 | 21 | 9.x | |




- Installation
- Usage
* Tracking page views
* Adding info or customizing automatic page view tracking
* Tracking events
* Managing user consent: opt-in/opt-out for tracking & cookies
* Tracker API
- Plugins
* Form analytics
- Migration from @ngx-matomo/tracker and @ngx-matomo/router (version <= 4)
- Configuration reference
- FAQ
* How to use ngx-matomo-client without @angular/router?
* How to set page title?
* Should I include the tracking code provided by Matomo?
* How to disable tracking in some environments?
* How to exclude some routes from tracking
* How can I customize the inserted script tag?
* Can I use ngx-matomo-client with Server-side rendering (SSR) / Angular Universal?
* Can I use ngx-matomo-client with Tag Manager?
* How to define configuration asynchronously? (HTTP fetch...)
* How can I test my components which uses MatomoTracker or other Matomo features?
* Can I use ngx-matomo-client in a zoneless Angular application?
* My app freezes during route changes — how can I fix it?
- Roadmap
- Contributing
- Launch demo app
Run ng add ngx-matomo-client
This will prompt you for some information such as your Matomo's server address and site ID. You can find your site ID in
Matomo admin panel.
This command will set up basic configuration into your application, typically in main.ts.
Use ng add ngx-matomo-client --project [project] to specify project name in case you have a multi-project workspace.
You can then take a look at configuration reference for fine-grained set-up.
If you're not using Angular CLI, follow these instructions instead
Run npm install --save ngx-matomo-client or yarn add ngx-matomo-client then
manually import Matomo into your Angular application:
``ts
import { provideMatomo } from 'ngx-matomo-client';
await bootstrapApplication(RootComponent, {
providers: [
provideMatomo({
siteId: 1,
trackerUrl: 'http://my-matomo-instance',
}),
],
});
`
Or, if you use @NgModule:
`ts
import { MatomoModule } from 'ngx-matomo-client';
@NgModule({
imports: [
MatomoModule.forRoot({
siteId: 1,
trackerUrl: 'http://my-matomo-instance',
}),
],
})
export class AppModule {}
`
Take a look at configuration reference for all available configuration properties.
> Note: in this documentation, all code examples use imports from ngx-matomo-client because this is the most@angular/router
> common use case.
> If you don't have in you application, you must import from ngx-matomo-client/core instead.
>
> See FAQ for more details.
Enable automatic page view tracking by adding withRouter() feature to provideMatomo():
`ts
import { provideMatomo, withRouter } from 'ngx-matomo-client';
await bootstrapApplication(RootComponent, {
providers: [
provideMatomo(
{
siteId: 1,
trackerUrl: 'http://my-matomo-instance',
},
withRouter(),
),
],
});
`
See equivalent configuration with
@NgModule
`ts
import { MatomoModule, MatomoRouterModule } from 'ngx-matomo-client';
@NgModule({
imports: [
MatomoModule.forRoot({
siteId: 1,
trackerUrl: 'http://my-matomo-instance',
}),
MatomoRouterModule.forRoot(),
],
})
export class AppModule {}
`
This will track every page view using Angular Router.
If you wish to manually track page views instead, inject MatomoTracker and call trackPageView() or othersetCustomUrl
desired methods (, setReferrerUrl...):
`typescript
import { MatomoTracker } from 'ngx-matomo-client';
@Component({
selector: 'app-example',
templateUrl: './example.component.html',
styleUrls: ['./example.component.scss'],
})
export class ExampleComponent implements OnInit {
private readonly tracker = inject(MatomoTracker);
ngOnInit() {
this.tracker.trackPageView();
}
}
`
By default, Matomo detects page title and url. Page title is read from Angular's Title service (which itself is filled with route's 'title' key) if available, or from document title. You may override defaults as described below.
#### Using route data
1. Configure ngx-matomo-client to read from route data:
`ts
import { provideMatomo, withRouter, withRouteData } from 'ngx-matomo-client';
await bootstrapApplication(RootComponent, {
providers: [
provideMatomo(
{
/ ... /
},
withRouter(),
withRouteData(), // Add this feature
),
],
});
`
By default, Matomo properties are looked-up at matomo key of Angular route data.withRouteData
You may define another key by providing it as an argument to :
`ts
// provideMatomo()
withRouteData('myCustomKey');
// Route data:
const routes: Routes = [
{
path: 'hello',
component: HelloComponent,
data: {
myCustomKey: {
ecommerce: {
productSKU: '12345',
},
} as MatomoRouteData,
},
},
];
`
See equivalent configuration with
@NgModule
`ts
import { MatomoModule, MatomoRouterModule, MatomoRouteDataInterceptor } from 'ngx-matomo-client';
@NgModule({
imports: [
MatomoModule.forRoot({
/ ... /
}),
MatomoRouterModule.forRoot({
interceptors: [MatomoRouteDataInterceptor], // Add this configuration
}),
],
providers: [
// Optionally, define another key to look-up at:
{
provide: MATOMO_ROUTE_DATA_KEY,
useValue: 'myCustomKey',
},
],
})
export class AppModule {}
`
2. Then, declare route data under matomo key:
`ts
import { MatomoRouteData } from './route-data-interceptor';
const routes: Routes = [
{
path: '',
component: HomeComponent,
title: 'My home page', // <-- Matomo will use this title by default, if available
data: {
matomo: {
title: 'My home page title for Matomo', // <-- You can override the title sent to Matomo
} as MatomoRouteData,
},
},
{
path: 'hello',
component: HelloComponent,
title: 'My home page', // <-- Matomo will use this title if available
data: {
matomo: {
ecommerce: {
productSKU: '12345',
productName: 'French baguette',
},
} as MatomoRouteData,
},
},
// Data can also be retrieve asynchronously using 'resolve':
{
path: ':productId',
component: ProductComponent,
resolve: {
matomo: async (route: ActivatedRouteSnapshot): Promise
// Load any asynchronous data
const product = await inject(MyProductService).loadProductData(route.params.productId);
return {
ecommerce: {
productSKU: product.sku,
},
};
},
},
},
];
`
Following properties are available:
| Property | Type |
| --------------------------- | -------- |
| title | string |ecommerce.productSKU
| | string |ecommerce.productName
| | string |ecommerce.productCategory
| | string |ecommerce.price
| | number |ecommerce.quantity
| | number |
#### Using custom interceptor
If you need custom logic to extract data, define a custom interceptor implementation.
An interceptor may be either a _function_ or a _class_:
`ts
import {
MatomoRouterInterceptor,
MatomoRouterInterceptorFn,
MatomoRouteInterceptorBase,
} from 'ngx-matomo-client';
/* A simple functional interceptor /
const myInterceptorFn: MatomoRouterInterceptorFn = (event: NavigationEnd) => {
const tracker = inject(MatomoTracker);
tracker.setDocumentTitle('My title');
tracker.setEcommerceView(/ ... /);
};
/* A class interceptor must implement MatomoRouterInterceptor /
@Injectable()
export class MySimpleInterceptor implements MatomoRouterInterceptor {
private readonly tracker = inject(MatomoTracker);
beforePageTrack(event: NavigationEnd): void {
this.tracker.setDocumentTitle('My title');
this.tracker.setEcommerceView(/ ... /);
}
}
/**
* For interceptors that need access to ActivatedRouteSnapshot, you may extend
* MatomoRouteInterceptorBase built-in class.
*/
@Injectable()
export class MyAsyncInterceptor extends MatomoRouteInterceptorBase
private readonly tracker = inject(MatomoTracker);
protected extractRouteData(route: ActivatedRouteSnapshot): string {
return route.paramMap.get('productId');
}
protected async processRouteData(productId: string): Promise
const product = await this.loadProductData(productId);
this.tracker.setEcommerceView(productId, product.name);
}
}
`
And provide it in your application:
`typescript
import { withRouterInterceptors, MatomoRouterInterceptor } from 'ngx-matomo-client';
await bootstrapApplication(RootComponent, {
providers: [
provideMatomo(
{
/ ... /
},
withRouter(),
// Add interceptors here:
withRouterInterceptors([myInterceptorFn, MySimpleInterceptor, MyAsyncInterceptor]),
),
],
});
`
See equivalent configuration with
@NgModule
`ts`
@NgModule({
imports: [
MatomoModule.forRoot({
/ ... /
}),
MatomoRouterModule.forRoot({
// Add interceptors here:
interceptors: [myInterceptorFn, MySimpleInterceptor, MyAsyncInterceptor],
}),
],
})
export class AppModule {}
You can track click events in templates using [matomoClickAction] directive:
`html`
type="button"
matomoClickCategory="myCategory"
matomoClickAction="myAction"
matomoClickName="myName"
[matomoClickValue]="42"
>
Example for tracking button clicks
You can also track any kind of events using [matomoTracker] directive:
`html
type="text"
matomoTracker="change"
matomoCategory="myCategory"
matomoAction="myAction"
matomoName="myName"
[matomoValue]="myValue"
/>
type="text"
[matomoTracker]="['focus', 'blur']"
matomoCategory="myCategory"
matomoAction="myAction"
matomoName="myName"
/>
type="text"
matomoTracker
#tracker="matomo"
matomoCategory="myCategory"
matomoAction="myAction"
matomoName="myName"
[matomoValue]="myValue"
(change)="tracker.trackEvent()"
/>
type="text"
matomoTracker
#tracker="matomo"
matomoCategory="myCategory"
matomoAction="myAction"
(focus)="tracker.trackEvent('focus')"
(blur)="tracker.trackEvent('blur')"
/>
`
_Note for standalone components users: all ngx-matomo-client components and directives are standaloneMATOMO_DIRECTIVES
and can be imported where you need them. You may also want to import all of them at once using ._
Matomo supports multiple options to allow requiring user consent for tracking.
To identify whether you need to ask for any consent, you need to determine whether your lawful basis for processing
personal data is "Consent" or "Legitimate interest", or whether you can avoid collecting personal data altogether.
#### Do not track
_Do not track_ feature is supported, just set acceptDoNotTrack configuration option.
Please note that _do-not-track_ setting is also configured server-side! You should likely set this setting here to match
your server-side configuration. In case users opt-in for _do-not-track_:
- If set to true here, users will not be tracked, independently of you server-side setting.false
- If set to here (the default), users will be tracked depending on your server setting, **but tracking requests
and cookies will still be created!**
#### Consent opt-in
By default, no consent is required. To manage consent opt-in, first set dedicated configuration option requireConsent to either 'cookie' or 'tracking':
- In the context of tracking consent no cookies will be used and no tracking request will be sent unless consent
was given. As soon as consent was given, tracking requests will be sent and cookies will be used.
- In the context of cookie consent tracking requests will be always sent. However, cookies will be only used if
consent for storing and using cookies was given by the user.
For integration with a consent opt-in form, you may want to use following MatomoTracker methods:
- isConsentRequired()setConsentGiven()
- / setCookieConsentGiven()rememberConsentGiven(hoursToExpire?: number)
- / rememberCookieConsentGiven(hoursToExpire?: number)forgetConsentGiven()
- / forgetCookieConsentGiven()hasRememberedConsent()
- / areCookiesEnabled()getRememberedConsent()
- / getRememberedCookieConsent()
See also example below on how to create a consent form. Example below is about creating an opt-out form, but it may be
easily adapted using methods listed above.
#### Consent opt-out
To manage consent opt-out, use dedicated methods MatomoTracker.optUserOut() and MatomoTracker.forgetUserOptOut().
A (very) simple form is provided through component.
For more advanced integration with a custom form, you may want to define your own component and use MatomoTracker
methods:
` To opt-out, please activate the checkbox below to receive an opt-out cookie.html`
`typescript
@Component({
selector: 'my-opt-out-form',
templateUrl: '...',
})
export class MatomoOptOutFormComponent {
optedOut$: Promise
constructor(private readonly tracker: MatomoTracker) {
this.optedOut$ = tracker.isUserOptedOut();
}
handleChange(optOut: boolean) {
if (optOut) {
this.tracker.optUserOut();
} else {
this.tracker.forgetUserOptOut();
}
this.optedOut$ = this.tracker.isUserOptedOut();
}
}
`
This example is adapted from
official guide
about how to create a custom opt-out form
All Matomo tracking features are available through MatomoTracker service. Please refer
to Matomo documentation for details.
`typescript
import { Component, inject } from '@angular/core';
import { MatomoTracker } from 'ngx-matomo-client';
@Component({
/ ... /
})
export class ExampleComponent {
private readonly tracker = inject(MatomoTracker);
myMethod() {
// Example of using e-commerce features:
this.tracker.setEcommerceView('product-SKU', 'My product name', 'Product category', 999);
this.tracker.addEcommerceItem('product-SKU');
this.tracker.trackEcommerceCartUpdate(999);
this.tracker.trackEcommerceOrder('order-id', 999);
// ... many more methods are available
}
}
`
Please note that most features (such as setEcommerceView) must be called beforetrackPageView! You may want to take a look
at how to use interceptors.
Form Analytics support is currently experimental. Please report any bugs, and pull requests are highly appreciated!
#### Configuration
Form analytics plugin is supported out-of-the-box. Add withFormAnalytics() feature to provideMatomo():
`ts
import { withFormAnalytics } from 'ngx-matomo-client/form-analytics';
await bootstrapApplication(RootComponent, {
providers: [
provideMatomo(
{}, // Your base configuration
withFormAnalytics(), // Add this feature
),
],
});
`
See equivalent configuration with
@NgModule
`ts
import { MatomoFormAnalyticsModule } from 'ngx-matomo-client/form-analytics';
@NgModule({
imports: [
NgxMatomoModule.forRoot({
/ ... /
}),
MatomoFormAnalyticsModule,
],
})
export class AppModule {}
`
#### Usage
Matomo client will automatically scan for forms in your pages after each page tracking.
If some forms are dynamically added in your components on another timing, you can use matomoTrackForm ormatomoTrackForms directives to track them:
`html`
If the content of your form is dynamic, and you want to correctly track inner form controls, you will have to rescan the
form after changes:
`html`
If a _container_ is dynamically toggled, you can track multiple descendant forms at once by using matomoTrackForms (note the final _s_) on the container:
`html`
To automatically track a form submit when an element is clicked (for example a non-submit button), add matomoTrackFormSubmit on the button:
`html`
You can also inject MatomoFormAnalytics service in your components and use low-level api directly.
See official guide at https://developer.matomo.org/guides/form-analytics.
and @ngx-matomo/router (version <= 4)Starting from version 5, this library is distributed as a single package named ngx-matomo-client instead@ngx-matomo/tracker
of and @ngx-matomo/router.
Run ng add ngx-matomo-client to migrate your code automatically.
To manually migrate your code:
1. In your package.json, replace @ngx-matomo/tracker dependency with ngx-matomo-clientpackage.json
2. In your , remove @ngx-matomo/router dependency@ngx-matomo/tracker
3. Replace all imports from or @ngx-matomo/router with imports from ngx-matomo-client instead.
Also, feel free to use the new NgModule-free way of providing ngx-matomo-client using provideMatomo() functionNgxMatomoModule
instead of importing and NgxMatomoRouterModule.
Find all options and features here
If you don't have @angular/router in your application, you will encounter errors when declaring importsngx-matomo-client
from .
Instead, you must use imports from ngx-matomo-client/core.
> This is because ngx-matomo-client is composed of two entry points:ngx-matomo-client/core
>
> - which contains core features and doesn't depend on @angular/routerngx-matomo-client/router
> - which brings router features and depends on @angular/routerngx-matomo-client
>
> For backward compatibility reasons, the global entrypoint is a shorthand that re-exports both of them (thus depending on @angular/router).
If automatic page view tracking is enabled, then you probably have nothing to do: the page title will be detected and
sent to Matomo.
- Customizing page title by setting title property of Angular route config is natively supported.delay
See Angular tutorial here: Setting the page title.
Please note this will not work if you set Matomo router option to -1.tracker.setDocumentTitle(title)
- For more complex logic, you may also define an interceptor that will call .
See dedicated section.
If you're not using automatic page view tracking, then you should manually call tracker.setDocumentTitle(title)tracker.trackPageView(title)
or .
No, by default ngx-matomo-client includes Matomo's tracking script for you, so **you don't need to copy/paste the
tracking code into your application**.
If you are not using the default configuration and set the initialization mode to 'manual', then you must include the tracking code yourself as explained on official guide.
You may want to disable tracker in dev environments to avoid tracking some unwanted usage: local dev usage, end-to-end
tests...
To do so just set the disabled property to true in your main configuration:
`typescript`
await bootstrapApplication(RootComponent, {
providers: [
provideMatomo({
/ ... /
disabled: !environment.production,
}),
],
});
See equivalent configuration with
@NgModule
`typescript`
@NgModule({
imports: [
MatomoModule.forRoot({
/ ... /
disabled: !environment.production,
}),
],
})
export class AppModule {}
If you are using automatic route tracking and want to ignore some routes, use
the exclude option of router configuration:
`ts`
await bootstrapApplication(RootComponent, {
providers: [
provideMatomo(
{
/ ... /
},
withRouter({
exclude: [/some-pattern$/],
}),
),
],
});
See equivalent configuration with
@NgModule
`ts`
@NgModule({
imports: [
MatomoModule.forRoot({
/ ... /
}),
MatomoRouterModule.forRoot({
exclude: [/some-pattern$/],
}),
],
})
export class AppModule {}
By default, Matomo's script is injected using a basic script tag looking
like