🚀 Building a Role Based Navigation in NextJS using Clerk

🚀 Building a Role Based Navigation in NextJS using Clerk

Originally posted on the House of Giants Blog: https://houseofgiants.com/blog/building-role-based-navigation-nextjs-clerk

We’ve been working on a few projects with fairly complex navigation systems here at House of Giants. Different user types need to see different parts of an application and are separate in what they can see based on the role that they’re assigned. As these applications grow these navigation systems can become unwieldy. We’ve developed a system that we think works pretty well.

At a high level, here are a few of the things we’ve found that make role-based navigation tricky:

  • ⚖️ Duplicated items across various roles
  • 😵💫 Hard-to-maintain code when you need to update shared elements
  • 🔐 Complex logic around who can see what
  • 🔄 Difficulty in adding new roles or items without breaking things

Let’s see how we can alleviate some of these issues.

🛠️ Step 1: A Modular Navigation Structure

Using a modular approach gives us the ability to change only what we need, only for the user types that are affected. Keeping logic separate ensures that changing one piece doesn’t create a litany of issues for other users.

1. Define Individual Navigation Items

Start by setting up a single source of truth for all navigation items. This keeps things consistent and eliminates redundancies. These are just basic objects, customize them and their data based on your needs.


Code snippet showing the navItems object structure.
navItems object structure

2. Create Reusable Navigation Sets

At this point we found it helpful to define some commonly used groupings. This lets us build different navigation layouts without repeating ourselves. ✨


Code snippet showing the navSets object structure.
navSets object structure

3. Organize Navigation into Sections

With items and sets defined, we can further organize the navigation into logical sections. This keeps everything structured and makes it easy to add or remove sections later. We’ve opted to create a new Set to further ensure that no duplicate routes are provided. A value in a set may only occur once, duplicates are omitted.

In our case, we’re adding a text value to our sections, this acts as the parent dropdown label for the set of navigation items.


Code snippet showing the sections object structure.
sections object structure

4. Configure Role-Based Access

Here’s where we map user roles to the sections they should see. This keeps role-based logic centralized and easy to adjust 🔒 – keep in mind here, we’re using Clerk.com role names here, yours may be different.


Code snippet showing roleConfig object structure.
roleConfig object structure

5. Build the Navigation Hook

We decided that building a custom hook here would serve us best when building the UI for the navigation. Our hook assembles the navigation based on the user’s role and will return only the sections relevant to each user.


Code snippet showing the getNavigationItems, and useNavigation hooks.

You’ll see above that we’re creating a function called getNavigationItems and passing this function the userRole from Clerk. We then define the sectionNames based on that userRole, or default to the nav a basic user would see, providing us with a clean fallback.

We then map over those section names and return the title as well as the nested navigation items.

Finally, we define our hook. We get the User’s role from Clerk using the sessionClaims object that is returned with await auth(), thus returning our navigation object, which will look something like this:


useNavigation response example.
useNavigation() response example

🌟 Why This Works

This setup has some awesome advantages:

  • Single Source of Truth 📚: All navigation items are defined once, cutting down on duplication.
  • Composable Structure 🧩: You can create new sections easily by reusing existing sets.
  • Easy Maintenance 🔧: Shared items only need updates in one place.
  • Scalable 📈: Adding new roles or navigation items is a breeze.
  • Type Safety ✅: Using keys helps catch typos and enables IDE autocompletion.
  • Logical Structure 📂: Sections are organized in a clear, modular way.

Example Usage

Here’s how you might use this navigation structure in a Sidebar component:

File: Sidebar.js


Code snipped of the Sidebar component structure.

📝 Best Practices

A few tips to keep your navigation organized and manageable:

  • 📁 Store navigation items in a separate configuration folder.
  • ✨ Use descriptive names for navigation sets and sections.
  • ✅ Use Set to avoid duplicates.
  • 🔒 Set defaults for unknown user

This approach provides a solid foundation for a scalable, maintainable navigation structure. By organizing navigation into modular components, you get a flexible system that grows with your app—without becoming an absolute nightmare to maintain.

Remember, the goal is to strike a balance between flexibility and simplicity. This setup does just that, making it practical for real-world applications. 👌

To view or add a comment, sign in

Explore topics