diff --git a/demo/assets/dbp-toolkit-demo.html.ejs b/demo/assets/dbp-toolkit-demo.html.ejs
index 108c34ae5b7e7da8f571949ffd36a0204f64dc90..999cc48c62420e9182a43326a2234ac54274da4d 100644
--- a/demo/assets/dbp-toolkit-demo.html.ejs
+++ b/demo/assets/dbp-toolkit-demo.html.ejs
@@ -36,44 +36,6 @@
     <link rel="preload" href="<%= getPrivateUrl('fonts/SourceSansPro-Semibold.otf.woff2') %>" as="font" type="font/woff2" crossorigin>
     <link rel="preload" href="<%= getPrivateUrl('fonts/SourceSansPro-Bold.otf.woff2') %>" as="font" type="font/woff2" crossorigin>
 
-    <!-- Matomo -->
-    <script type="text/javascript">
-        var _paq = window._paq || [];
-        _paq.push(['setCustomVariable', 1, "GitCommit", "<%= buildInfo.info %>", "visit"]);
-        _paq.push(['enableHeartBeatTimer']);
-        _paq.push(['disableCookies']);
-        _paq.push(['trackPageView']);
-        _paq.push(['enableLinkTracking']);
-
-        (function() {
-            var u="https://analytics.tugraz.at/";
-            _paq.push(['setTrackerUrl', u+'matomo.php']);
-            _paq.push(['setSiteId', '<%= matomoSiteId %>']);
-            var d=document, g=d.createElement('script'), s=d.getElementsByTagName('script')[0];
-            g.type='text/javascript'; g.async=true; g.defer=true; g.src=u+'matomo.js'; s.parentNode.insertBefore(g,s);
-        })();
-
-        // track changed locations
-        window.addEventListener('locationchanged', function(e) {
-            _paq.push(['setReferrerUrl', e.detail.referrerUrl]);
-            _paq.push(['setCustomUrl', location.href]);
-            // _paq.push(['setDocumentTitle', '']);
-            _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]);
-        });
-
-        // track errors
-        window.addEventListener('error', function(e) {
-            _paq.push(['trackEvent', 'Error', e.error.message + '\n' + e.error.stack]);
-        });
-    </script>
-    <!-- End Matomo Code -->
-
     <!-- Font related CSS -->
     <style>
         body {
@@ -134,6 +96,7 @@
     entry-point-url="<%= entryPointURL %>"
     base-path="<%= getUrl('') %>"
     keycloak-config='{"url": "<%= keyCloakBaseURL %>", "realm": "tugraz", "clientId": "<%= keyCloakClientId %>", "silentCheckSsoRedirectUri": "<%= getUrl('silent-check-sso.html') %>"}'
+    matomo-url="<%= matomoUrl %>" matomo-site-id="<%= matomoSiteId %>"
 ><dbp-loading-spinner></dbp-loading-spinner></<%= name %>>
 
 <!-- Error handling for too old browsers -->
diff --git a/demo/rollup.config.js b/demo/rollup.config.js
index 0887edd96927ed7008001bb0887fe36fdc02d3bc..d38a39d5b57297c5e353df6f34950807efeec509 100644
--- a/demo/rollup.config.js
+++ b/demo/rollup.config.js
@@ -43,7 +43,8 @@ let keyCloakServer = '';
 let keyCloakBaseURL = '';
 let keyCloakClientId = '';
 let pdfAsQualifiedlySigningServer = '';
-let matomoSiteId = 131;
+const matomoUrl = "https://analytics.tugraz.at/";
+const matomoSiteId = 131;
 let useTerser = buildFull;
 let useBabel = buildFull;
 let checkLicenses = buildFull;
@@ -185,6 +186,7 @@ export default {
             keyCloakClientId: keyCloakClientId,
             pdfAsQualifiedlySigningServer: pdfAsQualifiedlySigningServer,
             environment: build,
+            matomoUrl: matomoUrl,
             matomoSiteId: matomoSiteId,
             buildInfo: getBuildInfo()
           }
diff --git a/packages/app-shell/package.json b/packages/app-shell/package.json
index c8daf03f3b3420c4392fa037bd1a9398694cb672..d431e42fd6ffedaebadfd68171a98fae6ba954a2 100644
--- a/packages/app-shell/package.json
+++ b/packages/app-shell/package.json
@@ -23,6 +23,7 @@
     "rollup-plugin-consts": "^1.0.1",
     "rollup-plugin-copy": "^3.1.0",
     "rollup-plugin-delete": "^2.0.0",
+    "rollup-plugin-emit-ejs": "^3.1.0",
     "rollup-plugin-serve": "^1.0.1"
   },
   "dependencies": {
@@ -35,7 +36,8 @@
     "dbp-common": "^1.0.0",
     "dbp-language-select": "^1.0.0",
     "dbp-notification": "^1.0.0",
-    "dbp-person-profile": "^1.0.0"
+    "dbp-person-profile": "^1.0.0",
+    "dbp-matomo": "^1.0.0"
   },
   "scripts": {
     "build": "npm run build-local",
diff --git a/packages/app-shell/src/app-shell.js b/packages/app-shell/src/app-shell.js
index 4b0911b164f81246a9eeacf8529a673aad165786..012fd97a85d4f8484b90ef8eba5cdf0590509b2a 100644
--- a/packages/app-shell/src/app-shell.js
+++ b/packages/app-shell/src/app-shell.js
@@ -15,6 +15,7 @@ import {BuildInfo} from './build-info.js';
 import {TUGrazLogo} from './tugraz-logo.js';
 import {send as notify} from 'dbp-common/notification';
 import {appWelcomeMeta} from './dbp-app-shell-welcome.js';
+import {MatomoElement} from "dbp-matomo/src/matomo";
 
 
 const i18n = createI18nInstance();
@@ -58,6 +59,9 @@ export class AppShell extends ScopedElementsMixin(LitElement) {
         this._updateAuth = this._updateAuth.bind(this);
         this._loginStatus = 'unknown';
 
+        this.matomoUrl = '';
+        this.matomoSiteId = -1;
+
         this._attrObserver = new MutationObserver(this.onAttributeObserved);
     }
 
@@ -70,6 +74,7 @@ export class AppShell extends ScopedElementsMixin(LitElement) {
           'dbp-auth-menu-button': AuthMenuButton,
           'dbp-notification': Notification,
           'dbp-icon': Icon,
+          'dbp-matomo': MatomoElement,
         };
     }
 
@@ -222,6 +227,8 @@ export class AppShell extends ScopedElementsMixin(LitElement) {
             subtitle: { type: String, attribute: false },
             description: { type: String, attribute: false },
             _loginStatus: { type: Boolean, attribute: false },
+            matomoUrl: { type: String, attribute: "matomo-url" },
+            matomoSiteId: { type: Number, attribute: "matomo-site-id" },
         };
     }
 
@@ -638,6 +645,8 @@ export class AppShell extends ScopedElementsMixin(LitElement) {
             }
         }
 
+        this.track('renderActivity', activity.element);
+
         const elm = document.createElement(activity.element);
         this._onActivityAdded(elm);
         this._lastElm = elm;
@@ -658,6 +667,13 @@ export class AppShell extends ScopedElementsMixin(LitElement) {
         this._attrObserver.disconnect();
     }
 
+    track(action, message) {
+        const matomo = this.shadowRoot.querySelector(this.constructor.getScopedTagName('dbp-matomo'));
+        if (matomo !== null) {
+            matomo.track(action, message);
+        }
+    }
+
     _renderActivity() {
         const act = this.metadata[this.activeView];
         if (act === undefined)
@@ -713,6 +729,7 @@ export class AppShell extends ScopedElementsMixin(LitElement) {
         return html`
             <slot class="${slotClassMap}"></slot>
             <dbp-auth-keycloak lang="${this.lang}" url="${kc.url}" realm="${kc.realm}" client-id="${kc.clientId}" silent-check-sso-redirect-uri="${kc.silentCheckSsoRedirectUri || ''}" scope="${kc.scope || ''}"  idp-hint="${kc.idpHint || ''}" load-person try-login></dbp-auth-keycloak>
+            <dbp-matomo endpoint="${this.matomoUrl}" site-id="${this.matomoSiteId}"></dbp-matomo>
             <div class="${mainClassMap}">
             <div id="main">
                 <dbp-notification lang="${this.lang}"></dbp-notification>
diff --git a/yarn.lock b/yarn.lock
index 0e6c9b658cf11fc8b622b20c5e0d7ee0bf89cc96..7c6189aeaf5d68dd20fc53ec31c48d3912f3a00c 100644
--- a/yarn.lock
+++ b/yarn.lock
@@ -1131,6 +1131,11 @@
   resolved "https://registry.yarnpkg.com/@types/color-name/-/color-name-1.1.1.tgz#1c1261bbeaa10a8055bbc5d8ab84b7b2afc846a0"
   integrity sha512-rr+OQyAjxze7GgWrSaJwydHStIhHq2lvY3BOC2Mj7KnzI7XK0Uw1TOOdI9lDoajEbSWLiYgoo4f1R51erQfhPQ==
 
+"@types/ejs@^3.0.4":
+  version "3.0.4"
+  resolved "https://registry.yarnpkg.com/@types/ejs/-/ejs-3.0.4.tgz#8851fcdedb96e410fbb24f83b8be6763ef9afa77"
+  integrity sha512-ZxnwyBGO4KX/82AsFHTX82eMw0PsoBcIngEat+zx0y+3yxoNDJucAihg9nAcrc+g4Cwiv/4WcWsX4oiy0ySrRQ==
+
 "@types/estree@*":
   version "0.0.45"
   resolved "https://registry.yarnpkg.com/@types/estree/-/estree-0.0.45.tgz#e9387572998e5ecdac221950dab3e8c3b16af884"
@@ -1577,6 +1582,11 @@ async-limiter@~1.0.0:
   resolved "https://registry.yarnpkg.com/async-limiter/-/async-limiter-1.0.1.tgz#dd379e94f0db8310b08291f9d64c3209766617fd"
   integrity sha512-csOlWGAcRFJaI6m+F2WKdnMKr4HhdhFVBk0H/QbJFMCr+uO2kwohwXQPxw/9OCxp05r5ghVBFSyioixx3gfkNQ==
 
+async@0.9.x:
+  version "0.9.2"
+  resolved "https://registry.yarnpkg.com/async/-/async-0.9.2.tgz#aea74d5e61c1f899613bf64bda66d4c78f2fd17d"
+  integrity sha1-rqdNXmHB+JlhO/ZL2mbUx48v0X0=
+
 asynckit@^0.4.0:
   version "0.4.0"
   resolved "https://registry.yarnpkg.com/asynckit/-/asynckit-0.4.0.tgz#c79ed97f7f34cb8f2ba1bc9790bcc366474b4b79"
@@ -2996,6 +3006,13 @@ ee-first@1.1.1:
   resolved "https://registry.yarnpkg.com/ee-first/-/ee-first-1.1.1.tgz#590c61156b0ae2f4f0255732a158b266bc56b21d"
   integrity sha1-WQxhFWsK4vTwJVcyoViyZrxWsh0=
 
+ejs@^3.1.3:
+  version "3.1.5"
+  resolved "https://registry.yarnpkg.com/ejs/-/ejs-3.1.5.tgz#aed723844dc20acb4b170cd9ab1017e476a0d93b"
+  integrity sha512-dldq3ZfFtgVTJMLjOe+/3sROTzALlL9E34V4/sDtUd/KlBSS0s6U1/+WPE1B4sj9CXHJpL1M6rhNJnc9Wbal9w==
+  dependencies:
+    jake "^10.6.1"
+
 electron-to-chromium@^1.3.488:
   version "1.3.498"
   resolved "https://registry.yarnpkg.com/electron-to-chromium/-/electron-to-chromium-1.3.498.tgz#fd7188c8a49d6d0b5df1df55a1f1a4bf2c177457"
@@ -3449,7 +3466,7 @@ fast-glob@^2.2.6:
     merge2 "^1.2.3"
     micromatch "^3.1.10"
 
-fast-glob@^3.0.3:
+fast-glob@^3.0.3, fast-glob@^3.2.2:
   version "3.2.4"
   resolved "https://registry.yarnpkg.com/fast-glob/-/fast-glob-3.2.4.tgz#d20aefbf99579383e7f3cc66529158c9b98554d3"
   integrity sha512-kr/Oo6PX51265qeuCYsyGypiO5uJFgBS0jksyG7FUeCyQzNwYnzrNIMR1NXfkZXsMYXYLRAHgISHBz8gQcxKHQ==
@@ -3519,6 +3536,13 @@ file-saver@^2.0.2:
   resolved "https://registry.yarnpkg.com/file-saver/-/file-saver-2.0.2.tgz#06d6e728a9ea2df2cce2f8d9e84dfcdc338ec17a"
   integrity sha512-Wz3c3XQ5xroCxd1G8b7yL0Ehkf0TC9oYC6buPFkNnU9EnaPlifeAFCyCh+iewXTyFRcg0a6j3J7FmJsIhlhBdw==
 
+filelist@^1.0.1:
+  version "1.0.1"
+  resolved "https://registry.yarnpkg.com/filelist/-/filelist-1.0.1.tgz#f10d1a3ae86c1694808e8f20906f43d4c9132dbb"
+  integrity sha512-8zSK6Nu0DQIC08mUC46sWGXi+q3GGpKydAG36k+JDba6VRpkevvOWUW5a/PhShij4+vHT9M+ghgG7eM+a9JDUQ==
+  dependencies:
+    minimatch "^3.0.4"
+
 fill-range@^4.0.0:
   version "4.0.0"
   resolved "https://registry.yarnpkg.com/fill-range/-/fill-range-4.0.0.tgz#d544811d428f98eb06a63dc402d2403c328c38f7"
@@ -4798,6 +4822,16 @@ iterate-value@^1.0.0:
     es-get-iterator "^1.0.2"
     iterate-iterator "^1.0.1"
 
+jake@^10.6.1:
+  version "10.8.2"
+  resolved "https://registry.yarnpkg.com/jake/-/jake-10.8.2.tgz#ebc9de8558160a66d82d0eadc6a2e58fbc500a7b"
+  integrity sha512-eLpKyrfG3mzvGE2Du8VoPbeSkRry093+tyNjdYaBbJS9v17knImYGNXQCUV0gLxQtF82m3E8iRb/wdSQZLoq7A==
+  dependencies:
+    async "0.9.x"
+    chalk "^2.4.2"
+    filelist "^1.0.1"
+    minimatch "^3.0.4"
+
 jest-worker@^26.0.0:
   version "26.1.0"
   resolved "https://registry.yarnpkg.com/jest-worker/-/jest-worker-26.1.0.tgz#65d5641af74e08ccd561c240e7db61284f82f33d"
@@ -7283,6 +7317,16 @@ rollup-plugin-delete@^2.0.0:
   dependencies:
     del "^5.1.0"
 
+rollup-plugin-emit-ejs@^3.1.0:
+  version "3.1.0"
+  resolved "https://registry.yarnpkg.com/rollup-plugin-emit-ejs/-/rollup-plugin-emit-ejs-3.1.0.tgz#abf8804db49368517dcc0e8531e7e811c5d8dc11"
+  integrity sha512-GZR68O+tubvVH23KIn4487exrev1ojioH+QnD+VfqJE/Yczsq79yiLO8jp4eg3SulSbXerofz/7DnLfowMLdDg==
+  dependencies:
+    "@types/ejs" "^3.0.4"
+    ejs "^3.1.3"
+    fast-glob "^3.2.2"
+    fs-extra "^8.1.0"
+
 rollup-plugin-postcss@^3.1.2:
   version "3.1.3"
   resolved "https://registry.yarnpkg.com/rollup-plugin-postcss/-/rollup-plugin-postcss-3.1.3.tgz#b11f69a907cc497311f44da12030439545a7bd80"