<?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>Forem: Imamul Islam Ifti</title>
    <description>The latest articles on Forem by Imamul Islam Ifti (@imamifti056).</description>
    <link>https://forem.com/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.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F3134172%2F629772a4-7b11-4ed0-a3e2-af93f7a06e48.jpg</url>
      <title>Forem: Imamul Islam Ifti</title>
      <link>https://forem.com/imamifti056</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://forem.com/feed/imamifti056"/>
    <language>en</language>
    <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://forem.com/imamifti056/escaping-dependency-hell-how-i-migrated-a-legacy-cra-app-to-react-19-vite-2pnf</link>
      <guid>https://forem.com/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://forem.com/imamifti056/how-to-setup-tailwind-css-v415-with-vite-react-2025-updated-guide-3koc</link>
      <guid>https://forem.com/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>
