How to Hide Navbar Dropdown Items in ERPNext v16 by User Role

 —  Waliullah Thebo

If you’ve ever wanted a cleaner, less cluttered ERPNext interface for your end users — without touching core files or breaking future updates — this guide walks through exactly how to do it. We’ll hide items like Help, Display, About, and Edit Sidebar from the navbar dropdowns, and restrict that behavior so only Administrator (or users with the System Manager role) can still see the full menu.

This approach uses a custom Frappe app, a small JavaScript file, and Frappe’s built-in hooks system — no core file edits, no risky patches that get wiped out on the next bench update.

Why Hide ERPNext Navbar Items?

Out of the box, ERPNext’s navbar exposes several dropdown items in the Help (“?”) icon and the user profile icon — things like Help, About, Display (theme) settings, Edit Sidebar, Frappe Support links, and more. These are useful for administrators but often unnecessary — or even confusing — for regular staff, data-entry users, or students on an education-focused ERPNext instance. If you are just getting started with the system, check out our guide on ERPNext modules to understand what your users actually need to see.

Common reasons to hide them:

  • Simplify the UI for non-technical users (teachers, students, cashiers, sales staff)
  • Reduce support tickets caused by users accidentally changing themes or sidebar settings
  • Brand consistency — hide “Frappe Support” or “About” links that expose framework branding you don’t want end users clicking
  • Role-based UI — give admins full control while keeping everyone else’s screen simple

What We’re Building

A lightweight custom Frappe app (or an addition to your existing one) that:

  1. Detects the currently logged-in user’s role
  2. Hides specific navbar dropdown items only for non-admin users
  3. Leaves the full menu intact for Administrator / System Manager
  4. Keeps working even if Frappe re-renders the dropdown dynamically

Prerequisites

  • A working Frappe/ERPNext v16 bench setup
  • An existing custom app (this guide uses education_theme as the example app name — swap in your own)
  • Basic familiarity with hooks.py and the bench build / bench clear-cache workflow

Step 1: Inspect the Actual Dropdown Markup

Before writing any selector logic, open your browser’s DevTools and inspect the navbar dropdown items you want to hide. In ERPNext v16, each item is rendered as:

<div class="dropdown-menu-item" onclick="" style="cursor: pointer;">
  <a>
    <div class="menu-item-icon">
      <svg class="icon icon-sm" aria-hidden="true">
        <use href="#icon-monitor"></use>
      </svg>
    </div>
    <span class="menu-item-title">Display</span>
  </a>
</div>

Two important things to notice:

  • The class is .dropdown-menu-item, not Bootstrap’s default .dropdown-item. Using the wrong class means your selector matches nothing.
  • The onclick="" attribute is empty. Click handlers are bound via JavaScript event delegation, not inline HTML — so you can’t detect which button is which by reading onclick. The only reliable, version-safe way to identify an item is by its visible label inside .menu-item-title.

Step 2: Create the JavaScript File

Inside your custom app’s public/js/ folder, create hide_navbar_items.js:

// public/js/hide_navbar_items.js

// Visible labels to hide (case-insensitive, trimmed)
const ITEMS_TO_HIDE = [
  "help",
  "display",
  "about",
  "edit sidebar",
  "edit profile",
  "toggle theme",
  "frappe support",
  "reset desktop layout",
  // "log out",
];

function hide_navbar_items() {
  // Skip hiding for admins — System Manager sees the full menu

  // Only hide for non-Administrator users — Administrator sees everything
  if (frappe.session.user === "Administrator") {
    return;
  }
  // Only hide for System Manager users — Administrator sees everything

  if (frappe.user_roles.includes("System Manager")) {
    return;
  }

  $(".dropdown-menu-item").each(function () {
    const label = $(this)
      .find(".menu-item-title")
      .text()
      .trim()
      .toLowerCase();

    if (ITEMS_TO_HIDE.includes(label)) {
      $(this).hide();
    }
  });
}

// Run once the desk is ready
 $(document).on("app_ready", hide_navbar_items);
frappe.after_ajax(hide_navbar_items);

// Catch items that are lazily re-rendered (submenus, theme flyouts, etc.)
new MutationObserver(hide_navbar_items).observe(document.body, {
  childList: true,
  subtree: true,
});

Why role-based logic has to live in JavaScript, not CSS

A common mistake is trying to gate this behavior with CSS media queries or attribute selectors. That doesn’t work — CSS files served via app_include_css are static assets. Every logged-in user’s browser downloads the exact same stylesheet, regardless of who they are. CSS has no concept of “the current user.” Only JavaScript, running in the browser with access to frappe.session.user and frappe.user_roles, can make that decision at render time. If you’re currently using a CSS file with :has() selectors to hide these items unconditionally, remove it — it’ll hide the buttons for every user including admins, which defeats the purpose.

Step 3: Register the File in hooks.py

# hooks.py

app_name = "education_theme"
app_title = "Education Theme"
app_publisher = "thebonext"
app_description = "Education Theme"
app_email =  ["info@thebonext.com", "thebonext@gmail.com"]
app_license = "mit"

app_include_css = [
    "/assets/education_theme/css/education_theme.css",
]

app_include_js = [
    "education_theme.bundle.js",
    "/assets/education_theme/js/hide_navbar_items.js",
]

Step 4: Build, Clear Cache, and Test

bench build --app education_theme
bench --site your-site.local clear-cache
bench restart

Then hard-refresh your browser (Ctrl+Shift+R) — stale cached JS bundles are the number one reason a correct fix appears “not to work.” Log in as a regular user and confirm the items are hidden. Log in as Administrator (or a System Manager user) and confirm the full menu is intact.

Troubleshooting Checklist

Symptom Likely Cause
Nothing gets hidden for anyone Wrong class in selector (.dropdown-item instead of .dropdown-menu-item)
Items hidden for admins too Role check missing, or leftover CSS file still hiding items globally
Works once, then reappears on nested submenu Add the MutationObserver block — submenus are sometimes re-rendered
Works in English, not in other languages You’re matching translated label text — switch to icon-based matching (use[*|href$="icon-name"]) instead of .menu-item-title text
Still not working after code fix Browser/bundle cache — run bench build, bench clear-cache, then hard refresh

Important Security Note

This technique is cosmetic UI hiding, not access control. $(this).hide() only affects what’s rendered in the browser — a user could disable JavaScript, open DevTools, or call the underlying API method directly and still reach whatever functionality was behind that button. If any of the items you’re hiding expose something that genuinely needs to be restricted (not just tidied up visually), you’ll need proper role-based permissions or whitelisted method restrictions on the backend, not just a hidden button.

Conclusion

With a single custom-app JavaScript file and Frappe’s hooks.py, you can cleanly tailor the ERPNext navbar experience for different user roles — without patching core files that get overwritten on every update. This same pattern (identify by visible label, gate by frappe.user_roles, re-run on DOM mutations) can be extended to hide or show almost any dynamically rendered UI element in Frappe-based apps.

Frequently Asked Questions


Yes — swap frappe.user_roles.includes("System Manager") for any role name in your system, e.g. frappe.user_roles.includes("Sales User").


It’s low-risk since it doesn’t touch core files, but Frappe’s internal class names (.dropdown-menu-item, .menu-item-title) could change in a future major version. Always re-verify selectors after upgrading.


The same dropdown markup is generally reused on mobile, but always test separately — mobile breakpoints sometimes render menus differently.


Frappe supports overriding frappe.templates["navbar"] entirely from a custom app if you need deeper control than hiding individual items, but for simple show/hide logic, the approach in this guide is simpler and safer to maintain.

Tested on ERPNext v16 / Frappe Framework. Adjust class names and icon references if you’re on a different version.

Need help customizing your ERPNext UI?

Let TheboNext’s expert developers build a tailored, brand-consistent ERP experience for your team.

Explore Customization Services