My backend already guards /api/users with role:admin, and AdminRoute guards the /users page. So why does the sidebar also check the role before rendering the Users nav link? Because a permission error is a failure of the UI, not of the user. If someone can't use a feature, the interface shouldn't wave it in their face — it should never show it.
Here's the relevant part of my Layout:
const isAdmin = user?.role === 'admin';
// ...
<nav className="flex-1 space-y-1 px-3 py-6">
<NavLink to="/" end className={linkClass}>
Dashboard
</NavLink>
{isAdmin && (
<NavLink to="/users" className={linkClass}>
Users
</NavLink>
)}
</nav>
One boolean, one conditional render. The Users link exists in the DOM only for admins. A non-admin never sees a link that leads to a page they'll be bounced from — and just as importantly, a link they would be allowed to click on never leads to an API that 403s. Backend guard, route guard, and UI visibility all derive from the same user.role string, so the three can never disagree.
The sidebar does two more small jobs from that same user object:
const title = pageTitles[location.pathname] || 'SaaS Kit';
// ...
<div className="flex items-center gap-3">
<div className="flex h-10 w-10 items-center justify-center rounded-full bg-indigo-600 ...">
{initial}
</div>
<p className="truncate text-sm font-medium text-white">{user?.name}</p>
<span className="...">{user?.role}</span>
</div>
<button onClick={handleLogout}>Logout</button>
The header shows the current page title from a tiny pageTitles map keyed on location.pathname — Dashboard, Users, fallback to "SaaS Kit". The user card shows the name initial as an avatar, the name, and a role badge rendering the raw role string. That badge pulls double duty: it makes the user's permission level visible, which turns "why can't I see Users?" from a support ticket into a self-answering question. The logout button calls logout() from the auth context and navigates to /login.
None of this is clever. That's the point of a starter kit: the shell of the app — nav, header, user card, logout — is the part every project rebuilds identically, so it should be boring and already done. Pages plug into <Outlet />; the Layout handles everything around them.
This Layout ships in my Laravel + React SaaS Starter Kit ($19 one-time) — the full sidebar, role-conditional nav, and auth-wired shell above, ready to rename and restyle.
Top comments (0)