<?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: Imamul Islam Ifti</title>
    <description>The latest articles on DEV Community by Imamul Islam Ifti (@imamifti056).</description>
    <link>https://dev.to/imamifti056</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%2F3134172%2F629772a4-7b11-4ed0-a3e2-af93f7a06e48.jpg</url>
      <title>DEV Community: Imamul Islam Ifti</title>
      <link>https://dev.to/imamifti056</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/imamifti056"/>
    <language>en</language>
    <item>
      <title>How do you design permissions for a multi-tenant SaaS product?</title>
      <dc:creator>Imamul Islam Ifti</dc:creator>
      <pubDate>Wed, 26 Aug 2026 13:04:04 +0000</pubDate>
      <link>https://dev.to/imamifti056/how-do-you-design-permissions-for-a-multi-tenant-saas-product-1c9e</link>
      <guid>https://dev.to/imamifti056/how-do-you-design-permissions-for-a-multi-tenant-saas-product-1c9e</guid>
      <description>&lt;p&gt;I’ve been thinking a lot recently while working on migrating the &lt;/p&gt;

&lt;p&gt;&lt;code&gt;Role and Permission System&lt;/code&gt; of our organization-based SaaS product to a more scalable authorization architecture.&lt;br&gt;
Our product has multiple roles, different permissions, and different data scopes depending on the user and organization. As the system grows, simply adding more roles and if conditions across endpoints quickly becomes difficult to maintain.&lt;br&gt;
I’m exploring how large-scale products approach data scoping, delegated permissions, and centralized authorization; so any custom roles can be created and assign any combination of permissions to them.&lt;/p&gt;

&lt;p&gt;For those who have worked on authorization systems at scale:&lt;br&gt;
What architecture or approach has worked best for you?&lt;br&gt;
I’d especially love to hear about real-world lessons, common mistakes, or resources you’d recommend.&lt;/p&gt;

</description>
      <category>backend</category>
      <category>softwareengineering</category>
    </item>
    <item>
      <title>Escaping Dependency Hell: How I Migrated a Legacy CRA App to React 19 &amp; Vite</title>
      <dc:creator>Imamul Islam Ifti</dc:creator>
      <pubDate>Tue, 16 Dec 2025 17:42:23 +0000</pubDate>
      <link>https://dev.to/imamifti056/escaping-dependency-hell-how-i-migrated-a-legacy-cra-app-to-react-19-vite-2pnf</link>
      <guid>https://dev.to/imamifti056/escaping-dependency-hell-how-i-migrated-a-legacy-cra-app-to-react-19-vite-2pnf</guid>
      <description>&lt;p&gt;If you are maintaining a React application created 4 or 5 years ago, you know the feeling. You run &lt;code&gt;npm install&lt;/code&gt;, and the terminal bleeds red with vulnerability warnings. You try to upgrade one package, and three others break. You are trapped in Dependency Hell.&lt;/p&gt;

&lt;p&gt;After years of building features on top of Create React App (CRA) with React 17, React Router 5, Bootstrap 4, our project had accumulated significant technical debt.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The pain points were real:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Build times: 3-5 minutes for production builds&lt;/li&gt;
&lt;li&gt;Hot reload: Not there. Had to refresh manually to see the changes&lt;/li&gt;
&lt;li&gt;Security vulnerabilities: multiple npm audit warnings&lt;/li&gt;
&lt;li&gt;Modern tooling: Couldn't use Tailwind CSS v4 or Shadcn/ui&lt;/li&gt;
&lt;li&gt;Dead dependencies: Libraries like connected-react-router and redux-form hadn't been updated in years&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;I recently took on the challenge of upgrading this massive legacy project to the modern era of React 19, Vite, Tailwind v4, and React Router v7.&lt;/p&gt;

&lt;p&gt;Running &lt;code&gt;npm update&lt;/code&gt; was not an option. That path leads straight into dependency hell. Here is how I did it, avoiding the "npm update" trap, and the specific challenges I faced along the way.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Strategy: "The Vite Lift &amp;amp; Shift"
&lt;/h2&gt;

&lt;p&gt;Instead of trying to modernize in place, I decided to create a fresh Vite project and systematically move code over.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Dependency Purge
&lt;/h2&gt;

&lt;p&gt;Before moving code, I audited my dependencies. A lot of libraries from 2020 are dead or have better alternatives in 2025.&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Legacy Package&lt;/th&gt;
&lt;th&gt;New Equivalent&lt;/th&gt;
&lt;th&gt;Why?&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;react-scripts&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;Vite&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Instant dev server, native ESM support, faster builds, no Webpack 4 baggage.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;react-router-dom@v5&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;react-router-dom@v7&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;v5 is outdated and risky; v7 aligns with modern React and data routers.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;connected-react-router&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;em&gt;(Removed)&lt;/em&gt;&lt;/td&gt;
&lt;td&gt;React Router v6+ manages history internally; Redux no longer needs routing state.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;react-helmet&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;react-helmet-async&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;react-helmet&lt;/code&gt; is not compatible with React 18/19 concurrent &amp;amp; streaming rendering.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;jquery&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;em&gt;(Removed)&lt;/em&gt;&lt;/td&gt;
&lt;td&gt;React 18/19 and jQuery conflict; direct DOM mutation breaks React’s render model.&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h2&gt;
  
  
  Phase 1: Create a Clean Vite Shell
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;1.1. Initialize Vite (React)&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;npm create vite@latest my-new-app -- --template react
cd my-new-app
npm install
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;No &lt;code&gt;CRA&lt;/code&gt;. No &lt;code&gt;react-scripts&lt;/code&gt;. No &lt;code&gt;Webpack&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;1.2. Install Only “Safe” Dependencies&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Before touching legacy code, install:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;code&gt;axios&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;react-hook-form&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;date library (&lt;code&gt;moment&lt;/code&gt; or &lt;code&gt;date-fns&lt;/code&gt;)&lt;/li&gt;
&lt;li&gt;state management (Redux Saga)&lt;/li&gt;
&lt;li&gt;etc.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This prevents compounding errors later.&lt;/p&gt;

&lt;h2&gt;
  
  
  Phase 2: The Core Migration (Structure &amp;amp; Logic)
&lt;/h2&gt;

&lt;p&gt;Now that we have a fresh Vite shell, I copied my old &lt;code&gt;/src&lt;/code&gt; folder into the new project. But before touching any React code, I had to fix the "Physical" structure.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;2.1. The &lt;code&gt;index.html&lt;/code&gt; Shift&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;In CRA, index.html lived in the &lt;code&gt;/public&lt;/code&gt; folder, and Webpack "magically" injected the script.&lt;br&gt;
In Vite, &lt;code&gt;index.html&lt;/code&gt; is the front door. It must live in the root directory.&lt;br&gt;
I moved index.html from &lt;code&gt;/public&lt;/code&gt; to the root &lt;code&gt;/&lt;/code&gt;, and manually added the script tag to point to my React entry file (which Vite does not auto-inject):&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;&amp;lt;!-- index.html (Moved to Root) --&amp;gt;
&amp;lt;body&amp;gt;
  &amp;lt;div id="root"&amp;gt;&amp;lt;/div&amp;gt;
  &amp;lt;!-- Vite requires this explicit module entry --&amp;gt;
  &amp;lt;script type="module" src="/src/main.jsx"&amp;gt;&amp;lt;/script&amp;gt;
&amp;lt;/body&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;(Note: My imports were mostly relative, so I didn't need to configure path aliases yet to get the app running.)&lt;/p&gt;

&lt;h2&gt;
  
  
  Phase 3: The Big Breaking Changes
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;3.1. The Router Revolution (v5 to v7)&lt;/strong&gt;&lt;br&gt;
React Router completely redesigned its API:&lt;br&gt;
&lt;code&gt;Switch&lt;/code&gt; → &lt;code&gt;Routes&lt;/code&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;// OLD
import { Switch, Route } from 'react-router-dom';

&amp;lt;Switch&amp;gt;
  &amp;lt;Route path="/dashboard" component={Dashboard} /&amp;gt;
  &amp;lt;Route path="/profile" component={Profile} /&amp;gt;
&amp;lt;/Switch&amp;gt;

// NEW
import { Routes, Route } from 'react-router-dom';

&amp;lt;Routes&amp;gt;
  &amp;lt;Route path="/dashboard" element={&amp;lt;Dashboard /&amp;gt;} /&amp;gt;
  &amp;lt;Route path="/profile" element={&amp;lt;Profile /&amp;gt;} /&amp;gt;
&amp;lt;/Routes&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;code&gt;useHistory&lt;/code&gt; → &lt;code&gt;useNavigate&lt;/code&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;// OLD
import { useHistory } from 'react-router-dom';
const history = useHistory();
history.push('/login');

// NEW
import { useNavigate } from 'react-router-dom';
const navigate = useNavigate();
navigate('/login');
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;I used VS Code's find-and-replace with regex to batch convert this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Find: useHistory\(\)
Replace: useNavigate()

Find: history\.push\(
Replace: navigate(
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;3.2. Updating the State Machine: Redux&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;The most archaic part of my project was the Redux configuration. I was using the deprecated &lt;code&gt;createStore&lt;/code&gt;, manually composing middleware, and—worst of all—using &lt;code&gt;connected-react-router&lt;/code&gt; to force routing state into Redux.&lt;br&gt;
In modern React (v18/v19) combined with React Router v6/v7, we don't need routing data in our global store. Hooks like &lt;code&gt;useNavigate&lt;/code&gt; and &lt;code&gt;useLocation&lt;/code&gt; handle that natively.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;3.2.1 High-level comparison&lt;/strong&gt;&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Area&lt;/th&gt;
&lt;th&gt;Old Code (Legacy Redux)&lt;/th&gt;
&lt;th&gt;New Code (Redux Toolkit)&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Store creation&lt;/td&gt;
&lt;td&gt;&lt;code&gt;createStore + compose + applyMiddleware&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;configureStore&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Router integration&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;connected-react-router&lt;/code&gt; + &lt;code&gt;history&lt;/code&gt;
&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;Removed&lt;/strong&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;DevTools&lt;/td&gt;
&lt;td&gt;Manual / commented&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;Automatic&lt;/strong&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Middleware setup&lt;/td&gt;
&lt;td&gt;Manual, error-prone&lt;/td&gt;
&lt;td&gt;Safe defaults + explicit&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Redux best practices&lt;/td&gt;
&lt;td&gt;Optional&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;Enforced by default&lt;/strong&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Boilerplate&lt;/td&gt;
&lt;td&gt;High&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;Low&lt;/strong&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Future support&lt;/td&gt;
&lt;td&gt;Deprecated patterns&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;Official Redux path&lt;/strong&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;&lt;strong&gt;3.2.2 The biggest win: &lt;code&gt;configureStore&lt;/code&gt;&lt;/strong&gt;&lt;br&gt;
Before (manual &amp;amp; fragile):&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;// store.js (Legacy)
import { createStore, applyMiddleware, compose } from "redux";
import createSagaMiddleware from "redux-saga";
import { createBrowserHistory } from "history"; // &amp;lt;--- Dependency heavily coupled to DOM
import rootReducer from "./reducers";
import { routerMiddleware } from "connected-react-router"; // &amp;lt;--- Obsolete

const history = createBrowserHistory();
const sagaMiddleware = createSagaMiddleware();

export const store = createStore(
  rootReducer(history), // &amp;lt;--- Passing history into reducers
  compose(
    applyMiddleware(
      routerMiddleware(history), 
      sagaMiddleware
    )
  )
);
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Problems&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Easy to misconfigure&lt;/li&gt;
&lt;li&gt;No defaults&lt;/li&gt;
&lt;li&gt;DevTools must be wired manually&lt;/li&gt;
&lt;li&gt;No safety checks&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;After (The Modern Redux Toolkit):&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;I switched to &lt;code&gt;configureStore&lt;/code&gt; from &lt;code&gt;@reduxjs/toolkit&lt;/code&gt;. It automatically sets up the Redux DevTools extension and comes with better default middleware.&lt;br&gt;
I also completely removed &lt;code&gt;connected-react-router&lt;/code&gt; and the &lt;code&gt;history&lt;/code&gt; object dependency. The store is now purely for data, not navigation.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;configureStore({
  reducer: rootReducer,
  middleware: (getDefaultMiddleware) =&amp;gt;
    getDefaultMiddleware({ serializableCheck: false })
      .concat(sagaMiddleware),
});
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;What I unlocked for the future&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Because of this change, I can now:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Migrate reducers to createSlice&lt;/li&gt;
&lt;li&gt;Introduce RTK Query gradually&lt;/li&gt;
&lt;li&gt;Remove boilerplate reducers&lt;/li&gt;
&lt;li&gt;Drop Redux Saga later if needed&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;None of this was cleanly possible before.&lt;/p&gt;

&lt;h2&gt;
  
  
  Phase 4: Dependencies to Replace (Not Update)
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;4.1. react-helmet → react-helmet-async&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;The old &lt;code&gt;react-helmet&lt;/code&gt; breaks with React 18+'s concurrent rendering:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;// OLD (breaks in React 18+)
import { Helmet } from 'react-helmet';

// NEW (concurrent-safe)
import { Helmet, HelmetProvider } from 'react-helmet-async';

// In your root component
&amp;lt;HelmetProvider&amp;gt;
  &amp;lt;App /&amp;gt;
&amp;lt;/HelmetProvider&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;4.2. Remove &lt;code&gt;connected-react-router&lt;/code&gt;&lt;/strong&gt;&lt;br&gt;
React Router now handles history internally. I no longer need to sync it with Redux:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;import { connectRouter } from "connected-react-router";

// OLD: Had to pass history through Redux
const rootReducer = (history) =&amp;gt; combineReducers({
  router: connectRouter(history),
  auth: authReducer,
  // ... other reducers
});

// NEW: Just combine your reducers
const rootReducer = combineReducers({
  auth: authReducer,
  // ... other reducers
});
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;4.3. The Silent Cleanup&lt;/strong&gt;&lt;br&gt;
Surprisingly, my app didn't crash with the dreaded "Polyfill Errors" (missing Buffer or process) that plague many Vite migrations.&lt;br&gt;
Why? Because I updated my dependencies first.&lt;br&gt;
By jumping to &lt;code&gt;axios v1.13&lt;/code&gt;, and removing &lt;code&gt;jQuery&lt;/code&gt;, I effectively removed the libraries that relied on legacy Node.js globals. Modern libraries use browser-native standards (like &lt;code&gt;Uint8Array&lt;/code&gt;) instead of Node's &lt;code&gt;Buffer&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;However, I still had housekeeping tasks to finish the job:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;4.3.1. The Environment Variable Swap&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;code&gt;CRA&lt;/code&gt; uses &lt;code&gt;process.env.REACT_APP_.&lt;/code&gt; Vite uses &lt;code&gt;import.meta.env.VITE_.&lt;/code&gt;&lt;br&gt;
I had to rename all my &lt;code&gt;.env&lt;/code&gt; variables and update the code calls.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;// Old
const apiKey = process.env.REACT_APP_API_KEY;

// New
const apiKey = import.meta.env.VITE_API_KEY;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Phase 5: The "Silent" Breakers (Redux, CSS &amp;amp; SVGs)
&lt;/h2&gt;

&lt;p&gt;Even after the dependencies were fixed and the server started, the app wasn't quite right. I ran into three specific runtime issues where the "Old Way" simply didn't work in the "New World."&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;5.1. Redux Toolkit: The "Mutation" Police&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;In my old Redux setup, I was essentially running in "Wild West" mode. I could accidentally mutate state in a reducer, and React might still re-render, hiding the bug.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The Problem:&lt;/strong&gt;&lt;br&gt;
Redux Toolkit (which I switched to via &lt;code&gt;configureStore&lt;/code&gt;) comes with immutableCheck &lt;code&gt;middleware&lt;/code&gt; enabled by default. As soon as I clicked a button, the app crashed with:&lt;br&gt;
Invariant failed: A state mutation was detected inside a dispatch.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The Fix:&lt;/strong&gt;&lt;br&gt;
I had to find the reducers where I was directly modifying objects and rewrite them to return new objects (standard Redux pattern).&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;// Old Reducer (Previously worked silently, but bad practice)
case 'UPDATE_USER':
  state.currentUser.name = action.payload; // Direct mutation!
  return state;

// New Reducer (Immutable pattern)
case 'UPDATE_USER':
  return {
    ...state,
    currentUser: {
      ...state.currentUser,
      name: action.payload
    }
  };
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Note: If you use &lt;code&gt;createSlice&lt;/code&gt; in Redux Toolkit, you CAN mutate state (thanks to Immer), but since I was migrating legacy reducers, I had to fix the manual mutations.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;5.2. The CSS "Zebra Striping" Shift&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;I noticed my tables looked wrong. Previously, I used &lt;code&gt;:nth-of-type&lt;/code&gt; to color alternate rows. After the update (likely due to changes in how Bootstrap 5 or React renders the DOM tree), this selector started targeting columns instead of rows, or just behaving erratically.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The Fix:&lt;/strong&gt;&lt;br&gt;
I switched the selector to &lt;code&gt;:nth-child.&lt;/code&gt; It’s a subtle difference, but :nth-of-type looks at the element tag type, while &lt;code&gt;:nth-child&lt;/code&gt; looks at the strict index in the parent.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;5.3. The SVG Crash&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;code&gt;CRA&lt;/code&gt; (&lt;code&gt;Webpack&lt;/code&gt;) used to perform "magic" that allowed you to import SVGs as components or strings indiscriminately. &lt;code&gt;Vite&lt;/code&gt; is more explicit.&lt;br&gt;
When I tried to use my SVGs, the whole page crashed.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The Fix:&lt;/strong&gt;&lt;br&gt;
I had to install &lt;code&gt;vite-plugin-svgr&lt;/code&gt; and update my configuration to explicitly allow SVGs to be imported as React components.&lt;/p&gt;

&lt;p&gt;Update &lt;code&gt;vite.config.js&lt;/code&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;import svgr from "vite-plugin-svgr";

export default defineConfig({
  plugins: [
    react(),
    svgr(), // Enable SVG transformation
  ],
});
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Update the imports&lt;/strong&gt;&lt;br&gt;
Vite requires a query parameter &lt;code&gt;?react&lt;/code&gt; to know you want the component version, not the URL string&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;// ❌ Old (CRA Magic)
import { ReactComponent as Logo } from './logo.svg'; 

// ✅ New (Vite Explicit)
import Logo from './logo.svg?react'; 
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Phase 6: Infrastructure Polish &amp;amp; Deployment
&lt;/h2&gt;

&lt;p&gt;Once the app was running and bug-free, I did some final cleanup to make the developer experience (DX) better and ensure deployment worked.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;6.1. Setting up Path Aliases&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;My project originally used relative imports (&lt;code&gt;../../components/Button&lt;/code&gt;), which worked fine in Vite out of the box. However, I wanted to modernize the codebase to use cleaner imports.&lt;/p&gt;

&lt;p&gt;I updated &lt;code&gt;vite.config.js&lt;/code&gt; to recognize some paths:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;resolve: {
    alias: {
      '@': path.resolve(__dirname, './src'),
      '@assets': path.resolve(__dirname, './src/assets'),
      '@components': path.resolve(__dirname, './src/components'),
      '@Icons': path.resolve(__dirname, './src/Icons'),
    },
  },
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Before (The "Dot-Dot" Hell):&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;// Hard to read, breaks if I move this file
import Button from "../../../components/Button";
import { formatDate } from "../../../utils/dateHelpers";
import { UserContext } from "../../../context/UserContext";

const Profile = () =&amp;gt; { ... }
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;After (Clean &amp;amp; Refactor-Safe):&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;// Clean, readable, and works from anywhere
import Button from "components/Button";
import { formatDate } from "@/utils/dateHelpers";
import { UserContext } from "@/context/UserContext";

const Profile = () =&amp;gt; { ... }
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;6.2. The &lt;code&gt;build&lt;/code&gt; vs &lt;code&gt;dist&lt;/code&gt; Trap&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;This is a silent killer for CI/CD pipelines.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;CRA&lt;/strong&gt; compiles your production app into a folder named &lt;code&gt;build&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Vite&lt;/strong&gt; compiles your production app into a folder named &lt;code&gt;dist&lt;/code&gt;.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Conclusion: A Junior Dev’s Journey
&lt;/h2&gt;

&lt;p&gt;As a junior engineer, taking on a full-scale migration from Webpack to Vite felt like performing surgery while reading the manual. I’m proud that the app is now faster, cleaner, and running on modern React 19, but I also know I have a lot left to learn. This guide represents my current "best effort" solution. If you are a senior dev reading this and spot a security risk I missed, or a cleaner way to handle the Redux refactor, please drop a comment below. I’m sharing this to help others facing the same "Legacy Code" anxiety, but I’m also here to learn from you.&lt;/p&gt;

</description>
      <category>react</category>
      <category>vite</category>
      <category>webdev</category>
      <category>refactoring</category>
    </item>
    <item>
      <title>How to setup Tailwind CSS v4.1.5 with Vite + React (2025 updated guide)</title>
      <dc:creator>Imamul Islam Ifti</dc:creator>
      <pubDate>Wed, 07 May 2025 14:40:30 +0000</pubDate>
      <link>https://dev.to/imamifti056/how-to-setup-tailwind-css-v415-with-vite-react-2025-updated-guide-3koc</link>
      <guid>https://dev.to/imamifti056/how-to-setup-tailwind-css-v415-with-vite-react-2025-updated-guide-3koc</guid>
      <description>&lt;p&gt;Tailwind CSS v4+ has a new setup method when using Vite and React. This post walks you through the latest official installation process as outlined on the &lt;a href="https://tailwindcss.com/docs/installation/using-vite" rel="noopener noreferrer"&gt;Tailwind CSS docs&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  1. Prerequisites
&lt;/h2&gt;

&lt;p&gt;Make sure you have Node.js (v18+) and npm installed:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;node -v
npm -v
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  2. Create a new Vite + React project
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;npm create vite@latest my-portfolio -- --template react
cd my-portfolio
npm install
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  3. Install Tailwind CSS
&lt;/h2&gt;

&lt;p&gt;Install tailwindcss and &lt;a class="mentioned-user" href="https://dev.to/tailwindcss"&gt;@tailwindcss&lt;/a&gt;/vite via npm.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;npm install tailwindcss @tailwindcss/vite
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  4. Configure the Vite plugin
&lt;/h2&gt;

&lt;p&gt;Add the &lt;a class="mentioned-user" href="https://dev.to/tailwindcss"&gt;@tailwindcss&lt;/a&gt;/vite plugin to your Vite configuration file(vite.config.js).&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;import { defineConfig } from 'vite'
import react from '@vitejs/plugin-react'
import tailwindcss from '@tailwindcss/vite'
export default defineConfig({
  plugins: [
    react(), tailwindcss(),
  ],
})
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  5. Import Tailwind CSS
&lt;/h2&gt;

&lt;p&gt;Add an &lt;a class="mentioned-user" href="https://dev.to/import"&gt;@import&lt;/a&gt; to your CSS file (index.css) that imports Tailwind CSS.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;@import "tailwindcss";
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  6. Test
&lt;/h2&gt;

&lt;p&gt;In your app.jsx file replace the code with&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;&amp;lt;div className="min-h-screen flex items-center justify-center bg-black text-white text-4xl font-bold"&amp;gt;
      Tailwind is working!
&amp;lt;/div&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  7. Start your build process
&lt;/h2&gt;

&lt;p&gt;Run your build process with npm run dev or whatever command is configured in your package.json file.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;npm run dev
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Was this helpful? Let me know in the comments and follow for more!&lt;/p&gt;

</description>
      <category>webdev</category>
      <category>react</category>
      <category>tailwindcss</category>
      <category>vite</category>
    </item>
  </channel>
</rss>
