<?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: Faheem Ahmed</title>
    <description>The latest articles on DEV Community by Faheem Ahmed (@faheeem).</description>
    <link>https://dev.to/faheeem</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.us-east-2.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F4229408%2F640ba243-d206-468d-a5e5-b27b91d9b8b9.jpg</url>
      <title>DEV Community: Faheem Ahmed</title>
      <link>https://dev.to/faheeem</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/faheeem"/>
    <language>en</language>
    <item>
      <title>Building a Role-Based Dashboard in React with Just Context API and localStorage (No Backend)</title>
      <dc:creator>Faheem Ahmed</dc:creator>
      <pubDate>Fri, 06 Nov 2026 00:00:38 +0000</pubDate>
      <link>https://dev.to/faheeem/building-a-role-based-dashboard-in-react-with-just-context-api-and-localstorage-no-backend-559l</link>
      <guid>https://dev.to/faheeem/building-a-role-based-dashboard-in-react-with-just-context-api-and-localstorage-no-backend-559l</guid>
      <description>&lt;p&gt;I wanted a project that would force me to actually think about state management instead of just shipping another CRUD app with a database doing all the heavy lifting. So I built EMS — an admin/employee task management app where the entire persistence layer is &lt;code&gt;localStorage&lt;/code&gt;. No Express server, no MongoDB, nothing. Just React figuring out who you are and what you can see.&lt;/p&gt;

&lt;h2&gt;
  
  
  The core problem: one app, two very different UIs
&lt;/h2&gt;

&lt;p&gt;Admins and employees needed completely different dashboards, but I didn't want two separate apps or a mess of &lt;code&gt;if (role === 'admin')&lt;/code&gt; checks scattered across components. So the role lives in a single &lt;code&gt;AuthProvider&lt;/code&gt; that wraps the app, and routing/rendering decisions read off that context instead of prop-drilling a role flag through five layers of components.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;src/
├── context/
│   └── AuthProvider.jsx    # login state, user role, task data — all global
├── components/
│   ├── Auth/
│   ├── Dashboard/
│   │   ├── AdminDashboard.jsx
│   │   └── EmployeeDashboard.jsx
│   └── TaskList/
├── utils/
│   └── localStorage.js     # read/write helpers
└── App.jsx
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Once a user logs in, &lt;code&gt;AdminDashboard&lt;/code&gt; and &lt;code&gt;EmployeeDashboard&lt;/code&gt; are literally just different trees — no shared "mode" component trying to be clever about conditional rendering. Admin side creates and assigns tasks; employee side can accept, update, and mark tasks done/failed. Both read and write through the same task state, so an admin assigning a task and an employee updating its status are touching the same source of truth.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why localStorage instead of a backend
&lt;/h2&gt;

&lt;p&gt;Honestly — scope control. The point of this project was to get good at React's own state primitives, not to re-prove I can wire up an Express API (I've done that already, backend's usually Node/Express/MongoDB for me). &lt;code&gt;localStorage.js&lt;/code&gt; is a thin wrapper that serializes the task list and user session on every mutation, so refreshing the page doesn't nuke your state. It's obviously not multi-device or multi-user safe — two people can't share a task list across browsers — but for a single-session demo of role-based architecture, it does the job without extra infra getting in the way.&lt;/p&gt;

&lt;h2&gt;
  
  
  What I'd actually change next
&lt;/h2&gt;

&lt;p&gt;If I took this further I'd swap the localStorage layer for a real backend without touching the dashboard components at all — which is kind of the point of isolating persistence behind &lt;code&gt;utils/localStorage.js&lt;/code&gt; in the first place. Swap the implementation, keep the interface.&lt;/p&gt;

&lt;p&gt;Stack: React, Vite, Tailwind, Context API.&lt;/p&gt;

&lt;p&gt;Live: &lt;a href="https://ems.faheem.foo" rel="noopener noreferrer"&gt;ems.faheem.foo&lt;/a&gt;&lt;br&gt;
Code: &lt;a href="https://github.com/faheeemm/ems" rel="noopener noreferrer"&gt;github.com/faheeemm/ems&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Curious what people would do differently with the Context setup — feels like the kind of thing that works fine at this scale and falls apart the moment you add a third role.&lt;/p&gt;

</description>
      <category>react</category>
      <category>webdev</category>
      <category>javascript</category>
      <category>beginners</category>
    </item>
  </channel>
</rss>
