<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: David Madrid</title>
    <description>The latest articles on DEV Community by David Madrid (@dmadridy).</description>
    <link>https://dev.to/dmadridy</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F1401571%2Fa4e27974-00e9-4b60-bb50-633eae461ccf.JPG</url>
      <title>DEV Community: David Madrid</title>
      <link>https://dev.to/dmadridy</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/dmadridy"/>
    <language>en</language>
    <item>
      <title>Role-Based Sidebar Navigation in React Applications</title>
      <dc:creator>David Madrid</dc:creator>
      <pubDate>Wed, 11 Jun 2025 17:15:27 +0000</pubDate>
      <link>https://dev.to/dmadridy/role-based-sidebar-navigation-in-react-applications-415o</link>
      <guid>https://dev.to/dmadridy/role-based-sidebar-navigation-in-react-applications-415o</guid>
      <description>&lt;p&gt;Building dynamic sidebars is far from trivial. A maintainable and scalable solution requires a modern, well-structured approach — especially when dealing with multiple user roles and navigation scenarios. Without solid architecture, hardcoded components quickly become unmanageable as your application grows.&lt;/p&gt;

&lt;p&gt;Instead of relying on hardcoded items, you can define a sidebar configuration as data and render items dynamically based on user roles. Here’s a clean and extensible approach using TypeScript:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;type SidebarItem = {
  id: string
  title: string
  icon: keyof typeof Icons
  to: string
  allowedRoles: UserRole[]
  activeUrls?: string[]
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;const sidebarItems: SidebarItem[] = [
  {
    id: "dashboard",
    title: t("labels.dashboard"),
    icon: "dashboard",
    to: "/dashboard",
    allowedRoles: [UserRole.COLLABORATOR, UserRole.USER, UserRole.WORKER],
  },
  {
    id: "myJobs",
    title: t("labels.myJobs"),
    icon: "briefcase",
    to: "/my-jobs/current",
    activeUrls: ["/my-jobs/current", "/my-jobs/draft", "/my-jobs/completed"],
    allowedRoles: [UserRole.COLLABORATOR, UserRole.USER, UserRole.WORKER],
  },
  {
    id: "exploreServices",
    title: t("labels.exploreServices"),
    icon: "search",
    to: "/post-a-job",
    allowedRoles: [UserRole.USER],
  },
  {
    id: "myProfile",
    title: t("labels.myProfile"),
    icon: "user",
    to: "/my-profile",
    allowedRoles: [UserRole.WORKER],
  },
]
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The &lt;code&gt;SidebarItem&lt;/code&gt; type defines each item's structure — including &lt;code&gt;id&lt;/code&gt;, &lt;code&gt;title&lt;/code&gt;, &lt;code&gt;icon&lt;/code&gt;, destination route (&lt;code&gt;to&lt;/code&gt;), and the &lt;code&gt;allowedRoles&lt;/code&gt; that determine visibility. The optional &lt;code&gt;activeUrls&lt;/code&gt; helps match nested routes to highlight the correct item.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;🔧 &lt;code&gt;Icons&lt;/code&gt; is a key-value object where each key (e.g., "dashboard", "briefcase") maps to a corresponding icon component. This allows dynamic rendering like &lt;code&gt;const Icon = Icons[item.icon]&lt;/code&gt;.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;By defining each item in a structured array, you're free to map over them and render only what the current user is allowed to see:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;{sidebarItems.map(item =&amp;gt; {
  const Icon = Icons[item.icon]

  return (
    item.allowedRoles?.includes(user?.role as UserRole) &amp;amp;&amp;amp; (
      &amp;lt;Tooltip key={item.to}&amp;gt;
        &amp;lt;TooltipTrigger asChild&amp;gt;
          &amp;lt;Button
            asChild
            variant="ghost"
            className={cn(
              "text-sm",
              open &amp;amp;&amp;amp; "justify-start",
              (location.pathname === item.to ||
                item.activeUrls?.includes(location.pathname)) &amp;amp;&amp;amp;
                "bg-accent"
            )}
          &amp;gt;
            &amp;lt;Link to={item.to}&amp;gt;
              &amp;lt;Icon className={cn("h-5 w-5 shrink-0", open &amp;amp;&amp;amp; "mr-2")} /&amp;gt;
              &amp;lt;span className={open ? "block" : "hidden"}&amp;gt;
                {item.title}
              &amp;lt;/span&amp;gt;
            &amp;lt;/Link&amp;gt;
          &amp;lt;/Button&amp;gt;
        &amp;lt;/TooltipTrigger&amp;gt;
        &amp;lt;TooltipContent side="right" className={open ? "hidden" : ""}&amp;gt;
          &amp;lt;p&amp;gt;{item.title}&amp;lt;/p&amp;gt;
        &amp;lt;/TooltipContent&amp;gt;
      &amp;lt;/Tooltip&amp;gt;
    )
  )
})}

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;This conditionally renders each sidebar item only if the current user's role is included in the &lt;code&gt;allowedRoles&lt;/code&gt; array for that item.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;item.allowedRoles?.includes(...)&lt;/code&gt; checks whether &lt;code&gt;user.role&lt;/code&gt; exists in the &lt;code&gt;allowedRoles&lt;/code&gt; array. The optional chaining (&lt;code&gt;?.&lt;/code&gt;) prevents errors if &lt;code&gt;allowedRoles&lt;/code&gt; is undefined.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;user?.role&lt;/code&gt; as &lt;code&gt;UserRole&lt;/code&gt; safely accesses the user's role and asserts its type in TypeScript.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This setup is flexible enough to support localization, permissions, tooltips, role conditions, active state tracking, and more — all without making your component hard to read or extend.&lt;/p&gt;

</description>
    </item>
  </channel>
</rss>
