diff --git a/packages/app-shell/src/app-shell.js b/packages/app-shell/src/app-shell.js
index d110230dc18195c1257ac6c9b12deafc26abd1c1..95ea104586969c1fdc91950bb936d3141132227d 100644
--- a/packages/app-shell/src/app-shell.js
+++ b/packages/app-shell/src/app-shell.js
@@ -62,6 +62,7 @@ export class AppShell extends ScopedElementsMixin(DBPLitElement) {
         this._roles = [];
         this._i18n = createInstance();
         this.lang = this._i18n.language;
+        this._extra = [];
 
         this.matomoUrl = '';
         this.matomoSiteId = -1;
@@ -179,6 +180,7 @@ export class AppShell extends ScopedElementsMixin(DBPLitElement) {
                     return {
                         lang: this.lang,
                         component: '',
+                        extra: [],
                     };
                 },
             },
@@ -191,18 +193,20 @@ export class AppShell extends ScopedElementsMixin(DBPLitElement) {
                             return {
                                 lang: params.lang,
                                 component: '',
+                                extra: [],
                             };
                         },
                     },
                     {
                         name: 'mainRoute',
-                        path: '/:component',
+                        path: ['/:component', '/:component/(.*)'],
                         action: (context, params) => {
-                            // remove the additional parameters added by Keycloak
-                            let componentTag = params.component.toLowerCase().replace(/&.+/, '');
+                            let componentTag = params.component.toLowerCase();
+                            let extra = params[0] ? params[0].split('/') : [];
                             return {
                                 lang: params.lang,
                                 component: componentTag,
+                                extra: extra,
                             };
                         },
                     },
@@ -218,17 +222,20 @@ export class AppShell extends ScopedElementsMixin(DBPLitElement) {
                     let state = {
                         component: this.activeView,
                         lang: this.lang,
+                        extra: this._extra,
                     };
                     return state;
                 },
                 setState: (state) => {
                     this.updateLangIfChanged(state.lang);
                     this.switchComponent(state.component);
+                    this._extra = state.extra;
                 },
                 getDefaultState: () => {
                     return {
                         lang: 'de',
                         component: this.routes[0],
+                        extra: [],
                     };
                 },
             },
@@ -270,8 +277,10 @@ export class AppShell extends ScopedElementsMixin(DBPLitElement) {
     connectedCallback() {
         super.connectedCallback();
 
-        if (this.src) this.fetchMetadata(this.src);
         this.initRouter();
+        if (this.src) {
+            this.fetchMetadata(this.src);
+        }
     }
 
     /**
diff --git a/packages/app-shell/src/dbp-app-shell-welcome.js b/packages/app-shell/src/dbp-app-shell-welcome.js
index baea46045149f0d6d6ed6d3b92d62b1fe055d190..d16078bf20fbc119b049b644a5fc32b1da810510 100644
--- a/packages/app-shell/src/dbp-app-shell-welcome.js
+++ b/packages/app-shell/src/dbp-app-shell-welcome.js
@@ -61,7 +61,7 @@ class AppShellWelcome extends ScopedElementsMixin(LitElement) {
                 cursor: pointer;
                 text-decoration: none;
             }
-            
+
             h2 a {
                 white-space: nowrap;
             }
diff --git a/packages/app-shell/src/router.js b/packages/app-shell/src/router.js
index cbaefc12f3d6b60146482fbbb3c6dd5c74bed126..4f9461e04d0db06da2fb70d0d34d802d563aae48 100644
--- a/packages/app-shell/src/router.js
+++ b/packages/app-shell/src/router.js
@@ -1,6 +1,10 @@
 import UniversalRouter from 'universal-router';
 import generateUrls from 'universal-router/generateUrls';
 
+function stateMatches(a, b) {
+    return JSON.stringify(a, Object.keys(a).sort()) === JSON.stringify(b, Object.keys(b).sort());
+}
+
 /**
  * A wrapper around UniversalRouter which adds history integration
  */
@@ -31,28 +35,24 @@ export class Router {
 
         window.addEventListener('popstate', (event) => {
             this.setStateFromCurrentLocation();
-            this.dispatchLocationChanged();
+            this._dispatchLocationChanged();
         });
     }
 
+    async _getStateForPath(pathname) {
+        let isBasePath = pathname.replace(/\/$/, '') === this.router.baseUrl.replace(/\/$/, '');
+        if (isBasePath) {
+            return this.getDefaultState();
+        }
+        return this.router.resolve({pathname: pathname});
+    }
+
     /**
      * In case something else has changed the location, update the app state accordingly.
      */
     setStateFromCurrentLocation() {
-        const oldPathName = location.pathname;
-
-        this.router
-            .resolve({pathname: oldPathName})
+        this._getStateForPath(location.pathname)
             .then((page) => {
-                const newPathname = this.getPathname(page);
-                // In case of a router redirect, set the new location
-                if (newPathname !== oldPathName) {
-                    const referrerUrl = location.href;
-                    window.history.replaceState({}, '', newPathname);
-                    this.dispatchLocationChanged(referrerUrl);
-                } else if (this.isBasePath(oldPathName)) {
-                    page = this.getDefaultState();
-                }
                 this.setState(page);
             })
             .catch((e) => {
@@ -61,10 +61,6 @@ export class Router {
             });
     }
 
-    isBasePath(pathname) {
-        return pathname.replace(/\/$/, '') === this.router.baseUrl.replace(/\/$/, '');
-    }
-
     /**
      * Update the router after some internal state change.
      */
@@ -72,18 +68,21 @@ export class Router {
         // Queue updates so we can call this multiple times when changing state
         // without it resulting in multiple location changes
         setTimeout(() => {
-            const newPathname = this.getPathname();
-            const oldPathname = location.pathname;
-            if (newPathname === oldPathname) return;
-
-            const defaultPathname = this.getPathname(this.getDefaultState());
-            if (newPathname === defaultPathname && this.isBasePath(oldPathname)) {
-                return;
-            }
-
-            const referrerUrl = location.href;
-            window.history.pushState({}, '', newPathname);
-            this.dispatchLocationChanged(referrerUrl);
+            this._getStateForPath(location.pathname)
+                .then((page) => {
+                    const newState = this.getState();
+                    // if the state has changed we update
+                    if (!stateMatches(newState, page)) {
+                        const newPathname = this.getPathname();
+                        const referrerUrl = location.href;
+                        window.history.pushState({}, '', newPathname);
+                        this._dispatchLocationChanged(referrerUrl);
+                    }
+                })
+                .catch((e) => {
+                    // In case we can't resolve the location, just leave things as is.
+                    // This happens when a user enters a wrong URL or when testing with karma.
+                });
         });
     }
 
@@ -93,14 +92,15 @@ export class Router {
      * @param {string} pathname
      */
     updateFromPathname(pathname) {
-        this.router
-            .resolve({pathname: pathname})
+        this._getStateForPath(pathname)
             .then((page) => {
-                if (location.pathname === pathname) return;
-                const referrerUrl = location.href;
-                window.history.pushState({}, '', pathname);
-                this.setState(page);
-                this.dispatchLocationChanged(referrerUrl);
+                const oldState = this.getState();
+                if (!stateMatches(oldState, page)) {
+                    const referrerUrl = location.href;
+                    window.history.pushState({}, '', pathname);
+                    this.setState(page);
+                    this._dispatchLocationChanged(referrerUrl);
+                }
             })
             .catch((err) => {
                 throw new Error(`Route not found: ${pathname}: ${err}`);
@@ -117,7 +117,9 @@ export class Router {
      */
     getPathname(partialState) {
         const currentState = this.getState();
-        if (partialState === undefined) partialState = {};
+        if (partialState === undefined) {
+            partialState = {};
+        }
         let combined = {...currentState, ...partialState};
 
         try {
@@ -128,7 +130,7 @@ export class Router {
         }
     }
 
-    dispatchLocationChanged(referrerUrl = '') {
+    _dispatchLocationChanged(referrerUrl = '') {
         // fire a locationchanged event
         window.dispatchEvent(
             new CustomEvent('locationchanged', {