diff --git a/CHANGELOG.md b/CHANGELOG.md
index e11f198980578968df7259845f21b8188ac631e0..4c27eecf2bb022a6cd0f4ea0fbebe874ce797bc8 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -8,6 +8,9 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
 
 ### Added ###
 
+* `navbar.hideElementforRoles` function that hides a given element for certain
+  roles.
+
 ### Changed ###
 
 ### Deprecated ###
diff --git a/src/core/js/webcaosdb.js b/src/core/js/webcaosdb.js
index 1d6d5c8326d3f7dc0f311404f825206c9303462f..b622a1c879f3cf8aa53378e32f0a15f666ea7689 100644
--- a/src/core/js/webcaosdb.js
+++ b/src/core/js/webcaosdb.js
@@ -41,10 +41,10 @@ var globalError = function (error) {
     // ignore this particular error. It is caused by bootstrap-select which is
     // probably misusing the bootstrap 5 API.
     if (error.toString().startsWith("TypeError: this._element is undefined")) {
-      if (error && error.stack)
-        stack = "" + error.stack;
+        if (error && error.stack)
+            stack = "" + error.stack;
         if (stack.indexOf("dataApiKeydownHandler") > 0) {
-          return;
+            return;
         }
     }
 
@@ -306,6 +306,27 @@ this.navbar = new function () {
         }
     }
 
+    /**
+     * Hide the given element for certain roles
+     *
+     * @param {string|object} element - The HTML element to hide. May be either
+     *     a string to be used in a jquery, or the result of a jquery.
+     * @param {array} roles - List of roles for which the given element is
+     *     hidden.
+     */
+    this.hideElementForRoles = function (element, roles) {
+        var elt = element;
+        if (typeof (element) === "string") {
+            elt = $(elt);
+        }
+        const userRoles = getUserRoles();
+        if (userRoles.some(role => roles.includes(role))) {
+            elt.addClass("d-none");
+        } else {
+            elt.removeClass("d-none");
+        }
+    }
+
 }
 
 
diff --git a/test/core/js/modules/webcaosdb.js.js b/test/core/js/modules/webcaosdb.js.js
index 534dc86c485ccfac184bb05bac17d595addaa3d6..b43af58d4fef57aafd7d5d46998fc1a13725e868 100644
--- a/test/core/js/modules/webcaosdb.js.js
+++ b/test/core/js/modules/webcaosdb.js.js
@@ -1212,7 +1212,7 @@ QUnit.test("bindOnClick", function (assert) {
     $("body").append(form);
     $(form).append(submitButton);
     submitButton.click();
-  assert.equal(storage(), `FIND RECORD WHICH HAS A PROPERTY LIKE '*with double*' AND A PROPERTY LIKE '*and single*' AND A PROPERTY LIKE '*what\\'s wrong?*' AND A PROPERTY LIKE '*\\'*' AND A PROPERTY LIKE '*nothin\\'*' AND A PROPERTY LIKE '*"\\'bla*'`, "after5: stuff with quotation marks");
+    assert.equal(storage(), `FIND RECORD WHICH HAS A PROPERTY LIKE '*with double*' AND A PROPERTY LIKE '*and single*' AND A PROPERTY LIKE '*what\\'s wrong?*' AND A PROPERTY LIKE '*\\'*' AND A PROPERTY LIKE '*nothin\\'*' AND A PROPERTY LIKE '*"\\'bla*'`, "after5: stuff with quotation marks");
 
     // ... then with empty quotation marks. this will not trigger the query execution at all
     storage("not triggered");
@@ -1228,16 +1228,17 @@ QUnit.test("bindOnClick", function (assert) {
 QUnit.test("splitSearchTerms", function (assert) {
     assert.ok(queryForm.splitSearchTerms, "available");
     const cases = [
-      ["", []],
-      ['"', ['"']],
-      ["a", ["a"]],
-      ["a b", ["a", "b"]],
-      ["'a b'", ["a b"]],
-      ['"a b"', ["a b"]],
-      [`"with double" 'and single' "what's wrong?" ' "nothin'" "'bla`,
-          ["with double", "and single", "what's wrong?", "'", "nothin'", `"'bla`]],
+        ["", []],
+        ['"', ['"']],
+        ["a", ["a"]],
+        ["a b", ["a", "b"]],
+        ["'a b'", ["a b"]],
+        ['"a b"', ["a b"]],
+        [`"with double" 'and single' "what's wrong?" ' "nothin'" "'bla`,
+            ["with double", "and single", "what's wrong?", "'", "nothin'", `"'bla`]
+        ],
     ];
-    for(let testCase of cases) {
+    for (let testCase of cases) {
         assert.deepEqual(queryForm.splitSearchTerms(testCase[0]), testCase[1], `test case ${testCase[0]}`);
     }
 });
@@ -1245,7 +1246,7 @@ QUnit.test("splitSearchTerms", function (assert) {
 QUnit.test("initFreeSearch", function (assert) {
     const form = $('<form></form>');
     const inputGroup = $('<div class="input-group"></div>');
-    const textArea = $( '<textarea class="caosdb-f-query-textarea" name="query"></textarea>');
+    const textArea = $('<textarea class="caosdb-f-query-textarea" name="query"></textarea>');
     const submitButton = $('<input class="caosdb-search-btn" type="submit">');
     inputGroup.append(textArea);
     form.append(inputGroup).append(submitButton);
@@ -1885,6 +1886,35 @@ QUnit.test("toolbox example", function (assert) {
     assert.equal($('.caosdb-f-navbar-toolbox[data-toolbox-name="Tools"] button').length, 3, "three 'Tools' buttons");
 });
 
+QUnit.test("hide elements for roles", function (assert) {
+    navbar.add_button("TestButton1");
+    navbar.add_button("TestButton2");
+
+    // mock user role
+    $("#top-navbar").append(`<div class="caosdb-user-role">someRole</div>`);
+    navbar.hideElementForRoles($("button:contains('TestButton1')"), ["someOtherRole"]);
+    navbar.hideElementForRoles("button:contains('TestButton2')", ["someOtherRole"]);
+    // I don't have the role, so nothing is hidden:
+    assert.notOk($("button:contains('TestButton1')").hasClass("d-none"));
+    assert.notOk($("button:contains('TestButton2')").hasClass("d-none"));
+
+    // Add second role
+    $("#top-navbar").append(`<div class="caosdb-user-role second-role">someOtherRole</div>`);
+    // now I do
+    navbar.hideElementForRoles($("button:contains('TestButton1')"), ["someOtherRole"]);
+    navbar.hideElementForRoles("button:contains('TestButton2')", ["someOtherRole"]);
+    assert.ok($("button:contains('TestButton1')").hasClass("d-none"));
+    assert.ok($("button:contains('TestButton2')").hasClass("d-none"));
+
+    // Remove second role again
+    $(".second-role").remove();
+    // now I don't any more
+    navbar.hideElementForRoles($("button:contains('TestButton1')"), ["someOtherRole"]);
+    navbar.hideElementForRoles("button:contains('TestButton2')", ["someOtherRole"]);
+    assert.notOk($("button:contains('TestButton1')").hasClass("d-none"));
+    assert.notOk($("button:contains('TestButton2')").hasClass("d-none"));
+});
+
 QUnit.module("webcaosdb.js - version_history", {
     before: function (assert) {
         connection._init();