diff --git a/packages/common/index.js b/packages/common/index.js
index 1ef16d4c7dccb8d6e62fe30cfba5511f45785343..825ec1495f1196541b96677d9c0129d270a2401c 100644
--- a/packages/common/index.js
+++ b/packages/common/index.js
@@ -16,4 +16,5 @@ export {Spinner};
 export {InlineNotification};
 export {Translated};
 export * from './src/logger.js';
+export * from './src/utils.js';
 export {AdapterLitElement};
diff --git a/packages/common/src/utils.js b/packages/common/src/utils.js
new file mode 100644
index 0000000000000000000000000000000000000000..a3db6d6a4d9ebfc7561a919cfaeadff72efdac3b
--- /dev/null
+++ b/packages/common/src/utils.js
@@ -0,0 +1,17 @@
+/**
+ * Appends the second relative or absolute URL by treating
+ * the base URL as the root path. Unlike normal URL join which
+ * treats the host as root path.
+ * 
+ * http://example.com/foo + bar -> http://example.com/foo/bar
+ * http://example.com/foo/ + /bar -> http://example.com/foo/bar
+ *
+ * @param {string} baseURL The bas URL
+ * @param {string} addedURL The URL to append ot the baseURL
+ */
+export const combineURLs = (baseURL, addedURL) => {
+    if(!baseURL.endsWith('/')) {
+        baseURL += '/';
+    }
+    return new URL(addedURL.replace(/^\/+/, ''), baseURL).href;
+};
\ No newline at end of file
diff --git a/packages/common/test/unit.js b/packages/common/test/unit.js
index e53794049fbb3fcd71985417514b279a24e7a0b6..c72b634599f04ff175366f99ad82a506630a1d47 100644
--- a/packages/common/test/unit.js
+++ b/packages/common/test/unit.js
@@ -1,6 +1,7 @@
 import {expect, assert} from '@esm-bundle/chai';
 import * as utils from '../utils';
 import * as styles from '../styles';
+import {combineURLs} from '../';
 import '../jsonld.js';
 
 suite('utils', () => {
@@ -44,4 +45,18 @@ suite('utils', () => {
     test('getThemeCSS', () => {
         styles.getThemeCSS();
     });
+
+    test('combineURLs', () => {
+        assert.equal(combineURLs('http://example.org/foo', 'bar'), "http://example.org/foo/bar");
+        assert.equal(combineURLs('http://example.org/foo', '/bar'), "http://example.org/foo/bar");
+        assert.equal(combineURLs('http://example.org/foo/', '/bar/'), "http://example.org/foo/bar/");
+        assert.equal(combineURLs('http://example.org', '/bar'), "http://example.org/bar");
+        assert.equal(combineURLs('http://example.org', 'bar/'), "http://example.org/bar/");
+        assert.equal(combineURLs('http://example.org', ''), "http://example.org/");
+        assert.equal(combineURLs('http://example.org/bla', ''), "http://example.org/bla/");
+        assert.equal(combineURLs('http://example.org/bla/', ''), "http://example.org/bla/");
+        assert.equal(combineURLs('http://example.org', 'http://other.com'), "http://other.com/");
+        assert.equal(combineURLs('http://example.org', 'http://other.com/test'), "http://other.com/test");
+        assert.equal(combineURLs('http://example.org', 'http://other.com/test/'), "http://other.com/test/");
+    });
 });