Migration Won't Fix Your Broken App

Three fucking weeks. That's how long I wasted migrating to Vite expecting to be the office hero. Same slow loading, same sluggish clicks, same disappointed stakeholders staring at me during the demo.

Here's what I learned the hard way: build tools make your builds fast, not your app. Your dependencies are still bloated garbage. Your images are still unoptimized 8MB PNGs from designers. Your components still re-render the entire fucking DOM tree when someone types in a search box.

Your App Is Full of Garbage

CRA apps accumulate garbage over time:

Bundle analyzer showed the same massive chunks. All that dead weight just moved from webpack to Vite. My "optimized" build went from like 3.2MB to maybe 3.1MB. Embarrassing doesn't even cover it.

Webpack Logo

Vite Build Tool

Do This Before You Touch Any Code

Figure Out What's Actually Slow

Build your CRA app and actually look at the garbage you're shipping:

npm run build
ls -lah build/static/js/ | sort -k5 -hr

npm install --save-dev webpack-bundle-analyzer  
npx webpack-bundle-analyzer build/static/js/*.js

Bundle analyzer reveals the shit that's killing you:

First time I ran bundle analyzer, I wanted to throw my laptop out the window. Fifteen forgotten dependencies including some massive PDF library we tried once in 2023 and never bothered removing. Think it was like 60MB or something insane. We used react-pdf to display one document. One.

Measure Before You Migrate

npm install --save-dev lighthouse
npx lighthouse [your-dev-server-url] --view

Forget perfect Lighthouse scores. Focus on user experience:

  • LCP under 2.5s - when main content actually loads
  • FID under 100ms - how responsive clicks feel
  • CLS under 0.1 - how much the page jumps around

Test on slow connections or prepare to get roasted by users. Our app felt snappy in dev on fiber but took forever to load on actual 3G. Like 20+ seconds. Chrome DevTools → Network → "Slow 3G" - prepare to hate yourself. The Network Information API can help you adapt to connection speed.

Performance Auditing: Lighthouse shows you exactly what's killing your load times - JavaScript blocking the main thread, massive images loading before text, and CSS that's never used.

The Web Vitals documentation has more details if you want to go deeper, but honestly just focus on LCP and CLS - those are the ones that actually affect user experience.

Core Web Vitals: Focus on the metrics users actually feel - how long until they see content (LCP), how quickly clicks respond (FID), and how much the page jumps around (CLS).

Delete Your Unused Crap

Migration breaks imports anyway. Perfect time to delete dependencies you don't need:

npm install --save-dev depcheck
npx depcheck

First time I ran depcheck on our production app, I just stared at my screen. Twenty-seven unused packages. Including jsPDF (massive), react-virtualized (we switched to react-window ages ago), and three different fucking date libraries because apparently we couldn't make up our minds.

Bundle Analyzer Visualization

Delete this shit immediately:

  • moment.js - huge for date formatting that browsers already do
  • Full lodash imports - most of lodash is just worse Array methods now
  • Complete UI libraries - importing half a megabyte of Material-UI for two buttons
  • IE11 polyfills - Microsoft killed IE11, let it fucking die
  • Test utilities - leftover Enzyme imports from like 2019

Replace the big stuff:

// Old way: massive date library
import moment from 'moment';
const formatted = moment(date).format('YYYY-MM-DD');

// New way: browser handles it
const formatted = new Intl.DateTimeFormat('en-CA').format(date);

Look, if your team actually knows Material-UI inside and out and ships fast, maybe the bloat is worth it. I've watched teams spend months replacing MUI with some "lightweight" alternative, break half their shit, and end up with the same bundle size anyway. Pick your battles.

Use bundlephobia.com to check dependency sizes before installing. For dates, date-fns tree-shakes properly and Day.js is tiny. The webpack documentation explains tree-shaking in detail if you want to understand why imports matter so much.

Fix Your Imports

Modern bundlers hate bad imports. Fix them during migration:

// Old CRA way - loads everything
import Dashboard from './Dashboard';

// Modern way - lazy loading  
const Dashboard = lazy(() => import('./Dashboard'));

<Suspense fallback={<div>Loading...</div>}>
  <Dashboard />
</Suspense>
// Bad imports
import { Button } from '@mui/material';

// Better imports
import Button from '@mui/material/Button';

Migration Won't Magically Optimize Your Build

Copy this Vite config if you want bundle analysis:

// vite.config.js
import { visualizer } from 'rollup-plugin-visualizer';

export default {
  plugins: [
    process.env.ANALYZE && visualizer({ 
      filename: 'dist/stats.html',
      open: true 
    })
  ],
  build: {
    target: 'es2020',
    chunkSizeWarningLimit: 1000,
  }
};

Test before and after migration:

## Before
npm run build
ls -lah build/static/js/

## After  
npm run build
ls -lah dist/assets/

Don't trust your gut - "feels faster" is how I shipped a migration that actually made things slower. Lighthouse CI caught it weeks later when a client complained. Set up performance monitoring to catch regressions automatically.

What Actually Happens When You Migrate

Migration

Bundle Size

Build Speed

Dev Server

Runtime

Reality

CRA → Vite

~3.2MB → maybe 3.1MB

45s → 8s

35s → under 1s

Same shitty LCP

Dev server's amazing, prod isn't

CRA → Next.js

~3.2MB → 4MB+ with SSR

45s → 20s+

35s → few seconds

Better LCP, hydration hell

Way too complex for most apps

CRA → Parcel

~3.2MB → 3MB

45s → ~18s

35s → 3s

No improvement

Dead ecosystem, good luck

CRA → Webpack 5

Identical

45s → 42s

35s → 34s

Same garbage

Why did you waste your time

Advanced Optimizations Most People Skip

Build Tools

Every fucking migration tutorial stops at "congrats, you switched build tools!" like that solves anything. That's like changing your car's oil and expecting it to beat a Ferrari. Here's the shit that actually matters once your dev server stops being slow as hell.

Real User Monitoring: Modern tools expose performance issues that CRA's slow build cycle was hiding. Users clicking faster than your React can re-render shows you exactly which components are performance disasters.

Resource Loading Optimization

CRA loads everything at once with no prioritization. After migration, you can actually control what loads when using resource hints and modern loading strategies.

Next.js Logo

Preload Critical Stuff

Add this to your index.html using resource hints and preload directives:

<head>
  <!-- Load fonts first - stops text jumping around -->
  <link rel="preload" href="/fonts/main.woff2" as="font" type="font/woff2" crossorigin>
  
  <!-- Load hero image early -->
  <link rel="preload" href="/hero.webp" as="image">
  
  <!-- Connect to API faster -->
  <link rel="dns-prefetch" href="//api.yoursite.com">
</head>

Next.js handles this automatically:

import Image from 'next/image';

// Automatically optimized and preloaded
<Image 
  src="/hero.jpg"
  width={800}
  height={400}
  priority
  alt="Hero"
/>

Service Workers

CRA's service worker was fucking useless - it cached everything forever and gave you zero control. Spent 2 days debugging why users couldn't see updates because of aggressive caching. Modern tools like Workbox and Vite PWA actually let you cache intelligently:

// vite.config.js
import { VitePWA } from 'vite-plugin-pwa';

export default {
  plugins: [
    VitePWA({
      workbox: {
        runtimeCaching: [{
          urlPattern: /^https:\/\/api\./,
          handler: 'NetworkFirst'
        }]
      }
    })
  ]
};

Memory Leaks Become Obvious

Faster dev servers expose memory leaks like crazy. Suddenly users can click fast enough to trigger cleanup failures that CRA's sluggish reloads were hiding. Had one client's app crash after like 10 minutes - turns out we had setInterval calls running forever.

Fix Your useEffect Cleanup

Common memory leak:

useEffect(() => {
  const interval = setInterval(fetchData, 1000);
  // Missing cleanup - runs forever
}, []);

// Fixed version
useEffect(() => {
  const interval = setInterval(fetchData, 1000);
  return () => clearInterval(interval); // Actually clean up
}, []);

State Management Gets Messier Too

Most CRA apps have Redux setups that would make Dan Abramov cry. I've seen massive reducers that could be replaced with useState. Migration is perfect for cleaning up this architectural nightmare using Zustand or Jotai:

// Old way: giant context that re-renders everything
const AppContext = createContext();

// Better way: split contexts
const UserContext = createContext();
const ThemeContext = createContext();

Or just use Zustand:

import { create } from 'zustand';

const useStore = create((set) => ({
  user: null,
  setUser: (user) => set({ user })
}));

Images Still Suck After Migration

CRA doesn't optimize images. Modern tools like Vite can if you configure them, or use Next.js Image optimization for automatic optimization:

// vite.config.js
export default {
  assetsInclude: ['**/*.webp', '**/*.avif']
};

Or just use Next.js Image component and let it handle everything automatically. The Sharp library powers most image optimization tools.

Migration changes your build tool, not your performance disasters. That 8-second LCP? Still there. Those memory leaks? Still leaking. Images that are 4MB because designers don't understand web formats? Still fucking massive.

Use tools like ImageOptim, Squoosh, or Sharp to actually optimize images. The Performance API helps you measure real impact. And React DevTools Profiler shows you which components are killing performance.

Questions Everyone Asks About Migration

Q

Will this actually make my app faster?

A

Probably not. Migration makes your builds faster, not your app.

You'll get:

  • Dev server starts instantly instead of taking 30 seconds
  • Hot reload that actually works
  • Builds that don't timeout your CI

You might get:

  • Smaller bundles if you delete unused dependencies
  • Better performance if you fix your slow components
  • Faster loading if you optimize images

I spent 2 weeks migrating a client's app expecting to be the fucking hero. Demo day comes: "It loads the same speed as before." Because I moved like 3MB+ of bloated code to a slightly faster build tool. Stakeholders were not impressed.

Q

Components are still slow after migration

A

Build tools don't fix bad React code. Your slow components are still slow.

Common issues:

  • Everything re-renders on every state change (use useMemo)
  • API calls in render functions (move to useEffect)
  • Everything in one giant state object
  • 500-line components that should be split up

Migrated a dashboard that had multi-second click-to-response times. Every click re-rendered a shitload of components because some genius put user state, theme state, and navigation state in one giant context at the root level.

Install React DevTools Profiler and prepare to question all your life choices. Watched dozens of components re-render when someone typed one letter in a search box. React doesn't suck - your architecture does.

Q

When should I optimize - during or after migration?

A

During. You're already breaking all your imports anyway.

Do it in order:

  1. Get the migration working
  2. Make sure nothing broke
  3. Delete unused dependencies
  4. Optimize imports and bundle size
Q

Bundle is still huge after migration

A

You probably still have:

  • All of lodash for one debounce function
  • Multiple versions of the same dependencies
  • IE11 polyfills you don't need
  • Three different date libraries from old experiments

Run npx webpack-bundle-analyzer build/static/js/*.js to see what's bloating it.

Q

How do I know if migration helped?

A

Before:

ls -lah build/static/js/
npx lighthouse [your-dev-server-url] --view

After:

ls -lah dist/assets/  # Vite
npx lighthouse [your-vite-preview-url] --view

Focus on LCP, FID, CLS scores, not just bundle size.

Q

What dependencies should I replace?

A

Replace these during migration:

  • moment.jsdate-fns or native JS (saves ~280KB)
  • Full lodash → selective imports or native methods
  • Font Awesome everything → just the icons you use
  • UI libraries → selective imports

Don't go fucking insane like I did. Spent way too many hours replacing Material-UI with some "lightweight" alternative to save a tiny bit of space, broke half our forms, spent even more time fixing them, and ended up with a bundle barely smaller but way harder to maintain. Sometimes bloat is worth your sanity.

Q

Core Web Vitals got worse after migration

A

Common issues:

  • Forgot to preload fonts/images (CRA did this automatically)
  • Font loading changed, text jumps around
  • JavaScript loads in different order, blocks main thread

Fix:

<link rel="preload" href="/fonts/main.woff2" as="font" type="font/woff2" crossorigin>
<link rel="preload" href="/hero.webp" as="image">
Q

Dev server still slow after migration

A

Check:

  • Disable source maps if you don't need them
  • Too many files in src/
  • Old Node.js version (use 18+)
  • Antivirus scanning node_modules
Q

API calls slower after migration

A

This one's a classic gotcha:

  • Proxy config changed (Vite uses different syntax than webpack's proxy)
  • CORS errors everywhere because dev server changed ports
  • Environment variables broke (REACT_APP_API_URLVITE_API_URL)
  • Hot reload cancels in-flight requests and doesn't retry them

Spent hours wondering why my API calls were returning undefined. Turns out I forgot to update the env var names.

Does Your Migration Actually Work?

Performance Reality Check: Your bundle analyzer might show a 10% smaller build, but your users still wait 4 seconds for content to appear. Time to fix what actually matters.

Bundle size means fuck-all if your app still takes forever to load. I've watched developers throw a party for shaving a few KB from their bundle while their app takes way too long to show content. Here's what actually matters when measuring if your migration was worth the time you'll never get back.

Bundle Analysis

What Matters vs What Doesn't

Core Web Vitals (Users Actually Notice These)

Core Web Vitals docs have the details, but here's the summary. The PageSpeed Insights API can automate this testing:

LCP (Largest Contentful Paint) - Under 2.5s

  • When your main content actually fucking appears
  • Can improve a lot with proper image preloading
  • I broke image preloading during migration and LCP went from like 2s to over 4s. Clients definitely noticed. LCP optimization guide has detailed fixes.

FID (First Input Delay) - Under 100ms

  • How long users wait after clicking before anything happens
  • Modern tools usually fix this automatically
  • One client's FID went from decent to total shit because I fucked up code splitting and blocked the main thread for way too long. FID optimization covers common fixes.

CLS (Cumulative Layout Shift) - Under 0.1

  • How much your page bounces around like a trampoline
  • Migration almost always makes this worse initially
  • Spent an entire weekend debugging text that jumped around when fonts loaded because I removed font-display: swap. CLS optimization explains layout shift fixes.

Developer Experience (Team Productivity)

Time these before and after migration:

  • Dev server startup (should be under 5 seconds)
  • Hot reload (should be under 500ms)
  • Production builds (should be under 5 minutes)
time npm run dev
time npm run build

Bundle Optimization

Focus on gzipped sizes, not raw bundle size. Time to Interactive matters more than total JavaScript size.

Automate Performance Testing

Manual testing is garbage and will bite you in the ass. I pushed a migration that killed LCP badly and nobody caught it for weeks until a client complained that the site "feels broken now." Automation saves your reputation.

Set up Lighthouse CI:

npm install --save-dev @lhci/cli

Basic config:

// lighthouserc.js
module.exports = {
  ci: {
    collect: {
      url: ['http://localhost:4173']
    },
    assert: {
      assertions: {
        'categories:performance': ['error', {minScore: 0.8}]
      }
    }
  }
};

Track Real Users

Lighthouse tests from fast servers. Real users have slow phones and bad connections.

import { getCLS, getFID, getLCP } from 'web-vitals';

function sendToAnalytics(metric) {
  // Send to your analytics
  console.log(metric.name, metric.value);
}

getCLS(sendToAnalytics);
getFID(sendToAnalytics);  
getLCP(sendToAnalytics);

Performance Budgets

Set limits so you notice when things get worse:

// vite.config.js
export default {
  build: {
    chunkSizeWarningLimit: 1000, // 1MB warning
  }
};
// package.json
{
  \"bundlesize\": [
    {
      \"path\": \"./dist/assets/*.js\",
      \"maxSize\": \"250 kB\",
      \"compression\": \"gzip\"
    }
  ]
}

Performance gains decay like radioactive waste. Teams add dependencies, designers upload massive PNGs, junior devs write components that re-render everything. Without monitoring, you'll be back to slow load times within months guaranteed.

Set up continuous performance monitoring with tools like SpeedCurve, Calibre, or WebPageTest. The User Timing API lets you track custom metrics. And Performance Observer can monitor real user performance automatically.

Tools That Actually Help

Related Tools & Recommendations

tool
Similar content

Create React App is Dead: Why & How to Migrate Away in 2025

React team finally deprecated it in 2025 after years of minimal maintenance. Here's how to escape if you're still trapped.

Create React App
/tool/create-react-app/overview
100%
integration
Recommended

SvelteKit + TypeScript + Tailwind: What I Learned Building 3 Production Apps

The stack that actually doesn't make you want to throw your laptop out the window

Svelte
/integration/svelte-sveltekit-tailwind-typescript/full-stack-architecture-guide
70%
review
Recommended

Vite vs Webpack vs Turbopack: Which One Doesn't Suck?

I tested all three on 6 different projects so you don't have to suffer through webpack config hell

Vite
/review/vite-webpack-turbopack/performance-benchmark-review
68%
tool
Similar content

SvelteKit: Fast Web Apps & Why It Outperforms Alternatives

I'm tired of explaining to clients why their React checkout takes 5 seconds to load

SvelteKit
/tool/sveltekit/overview
63%
compare
Recommended

Remix vs SvelteKit vs Next.js: Which One Breaks Less

I got paged at 3AM by apps built with all three of these. Here's which one made me want to quit programming.

Remix
/compare/remix/sveltekit/ssr-performance-showdown
62%
tool
Similar content

Migrate from Create React App to Vite & Next.js: A Practical Guide

Stop suffering with 30-second dev server startup. Here's how to migrate to tools that don't make you want to quit programming.

Create React App
/tool/create-react-app/migration-guide
58%
tool
Similar content

Astro Overview: Static Sites, React Integration & Astro 5.0

Explore Astro, the static site generator that solves JavaScript bloat. Learn about its benefits, React integration, and the game-changing content features in As

Astro
/tool/astro/overview
51%
integration
Similar content

Claude API, Shopify, & React: Full-Stack Ecommerce Automation Guide

Navigate the complexities of integrating Claude API with Shopify and React for ecommerce automation. Learn real-world architecture, authentication solutions, an

Claude API
/integration/claude-api-shopify-react/full-stack-ecommerce-automation
45%
tool
Similar content

React Router Overview: The Routing Library That Actually Works

Comprehensive overview of React Router, the essential routing library for React applications. Learn its core functionality, history, new 'framework mode' in v7,

React Router
/tool/react-router/overview
45%
howto
Similar content

Angular to React Migration Guide: Convert Apps Successfully

Based on 3 failed attempts and 1 that worked

Angular
/howto/convert-angular-app-react/complete-migration-guide
41%
integration
Recommended

Bun + React + TypeScript + Drizzle Stack Setup Guide

Real-world integration experience - what actually works and what doesn't

Bun
/integration/bun-react-typescript-drizzle/performance-stack-overview
40%
howto
Recommended

Migrate React 18 to React 19 Without Losing Your Sanity

The no-bullshit guide to upgrading React without breaking production

React
/howto/migrate-react-18-to-19/react-18-to-19-migration
40%
alternatives
Recommended

React Alternatives That Won't Destroy Your Team

depends on React

React
/alternatives/react/migration-ready-alternatives
40%
integration
Recommended

I Spent Two Weekends Getting Supabase Auth Working with Next.js 13+

Here's what actually works (and what will break your app)

Supabase
/integration/supabase-nextjs/server-side-auth-guide
39%
tool
Recommended

Vite - Build Tool That Doesn't Make You Wait

Dev server that actually starts fast, unlike Webpack

Vite
/tool/vite/overview
37%
tool
Recommended

TypeScript - JavaScript That Catches Your Bugs

Microsoft's type system that catches bugs before they hit production

TypeScript
/tool/typescript/overview
33%
tool
Similar content

Qwik Overview: Instant Interactivity with Zero JavaScript Hydration

Skip hydration hell, get instant interactivity

Qwik
/tool/qwik/overview
32%
review
Recommended

Which JavaScript Runtime Won't Make You Hate Your Life

Two years of runtime fuckery later, here's the truth nobody tells you

Bun
/review/bun-nodejs-deno-comparison/production-readiness-assessment
30%
compare
Recommended

Which Static Site Generator Won't Make You Hate Your Life

Just use fucking Astro. Next.js if you actually need server shit. Gatsby is dead - seriously, stop asking.

Astro
/compare/astro/nextjs/gatsby/static-generation-performance-benchmark
29%
tool
Recommended

Fix Astro Production Deployment Nightmares

integrates with Astro

Astro
/tool/astro/production-deployment-troubleshooting
29%

Recommendations combine user behavior, content similarity, research intelligence, and SEO optimization