Explore Blogs

Migrating from Create React App (CRA) to Vite: A complete guide

bpi-cra-to-vite

Create React App served its purpose, but modern development needs better speed, flexibility, and transparency. Vite delivers on all fronts. In this post, you'll learn how to migrate your existing CRA project to Vite — including support for React Router, Redux or Zustand, TypeScript, testing with Vitest, Sass, PWA, and ESLint/Prettier.

If you've built a react app with Create React App (CRA), chances are you've started running into its limitations — or you've heard the ecosystem is moving on. CRA is officially deprecated, and Vite has quickly become the go-to replacement for a faster, modern development experience. If you're looking to migrate an existing CRA app to Vite, this guide is for you.

For TypeScript users, the setup instructions can be found at the bottom of this post.

Why to migrate to Vite?

Create React App gave you a zero-config starting point, but that convenience came with long build times, outdated dependencies, and a rigid Webpack setup. Vite solves those issues by leveraging native ES modules and lightning-fast bundling via ESBuild.

With Vite, you get:

  • Near-instant dev server startup
  • Fast hot module replacement (HMR)
  • Cleaner and more modern configuration
  • Better support for modern libraries like React Router v6, Zustand, and Vitest

If you're ready to switch, here's how to do it properly.

Clean up the CRA boilerplate

Before you migrate, remove CRA-specific files that Vite won't use. These include:

  • reportWebVitals.js
  • setupTests.js (you’ll replace this later)
  • logo.svg and any default CRA images
  • The service worker files, unless you plan to use PWA

Also simplify your src/main.jsx (or index.js) to remove unnecessary imports.

Install Vite and React dependencies

First, uninstall react-scripts:

npm uninstall react-scripts

Then install Vite and core React libraries:

npm install --save-dev vite
npm install react react-dom

You'll also want the Vite plugin for React (which handles Fast Refresh and JSX):

npm install --save-dev @vitejs/plugin-react

Create a vite.config.js

This file replaces CRA's invisible Webpack config. Here's a basic Vite config to get started:

// vite.config.js
import { defineConfig } from 'vite';
import react from '@vitejs/plugin-react';

export default defineConfig({
  plugins: [react()],
});

You can add more config later if needed — Vite is very flexible.

Move and edit your index.html

Vite doesn't use the public folder in the same way as CRA. Move public/index.html to the root of your project and update it like this:

<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <title>My Vite React App</title>
  </head>
  <body>
    <div id="root"></div>
    <script type="module" src="/src/main.jsx"></script>
  </body>
</html>

This gives Vite full control over your HTML file and lets you take advantage of its fast refresh and module loading features.

Set up react router

If your app uses React Router, you don't need to change much — just make sure your main.jsx wraps the app in a BrowserRouter:

import React from 'react';
import ReactDOM from 'react-dom/client';
import { BrowserRouter } from 'react-router-dom';
import App from './App';

ReactDOM.createRoot(document.getElementById('root')).render(
  <BrowserRouter>
    <App />
  </BrowserRouter>
);

Install it if you haven't yet:

npm install react-router-dom

Also, make sure to handle Vite's dev server fallback correctly by adding this to your vite.config.js:

server: {
  historyApiFallback: true,
}

Integrate redux or zustand (Optional)

Redux

If you use Redux, your setup won't change much. Just reinstall the packages:

npm install redux react-redux

Wrap your app in the <Provider> like you did in CRA:

import { Provider } from 'react-redux';
import store from './store';

<Provider store={store}>
  <App />
</Provider>;

Just make sure to replace any process.env.NODE_ENV with import.meta.env.MODE.

Zustand

Zustand works out of the box with Vite, no changes required:

npm install zustand

Keep using it like you did in CRA — Vite plays well with modern state libraries.

Set up testing with Vitest

CRA uses Jest, but Vite doesn't. Instead, install Vitest, which is faster and more Vite-native:

npm install --save-dev vitest @testing-library/react jsdom

Update vite.config.js:

test: {
  environment: 'jsdom',
},

Create a vitest.setup.js if you need to configure testing-library, and update your test scripts in package.json:

"scripts": {
  "test": "vitest"
}

Vitest uses the same test syntax as Jest, so most of your tests will work without modification.

Add Sass and CSS modules support

Vite has built-in support for CSS modules and works great with Sass.

Install Sass:

npm install sass

Then import .scss or .module.scss files like this:

import styles from './Button.module.scss'

<button className={styles.primary}>Click</button>

No extra config needed.

ESLint and Prettier setup

Vite doesn't include ESLint by default. If you had ESLint and Prettier in CRA, reinstall them:

npm install --save-dev eslint prettier eslint-plugin-react eslint-config-prettier

Create .eslintrc.json:

{
  "extends": ["react-app", "plugin:react/recommended", "prettier"]
}

Optional .prettierrc:

{
  "singleQuote": true,
  "semi": false
}

Add linting to your scripts:

"lint": "eslint ./src --ext .js,.jsx,.ts,.tsx"

Add PWA support (Optional)

If your CRA app used a service worker, you can replicate that with vite-plugin-pwa.

Install the plugin:

npm install --save-dev vite-plugin-pwa

Update your Vite config:

import { VitePWA } from 'vite-plugin-pwa'

export default defineConfig({
  plugins: [react(), VitePWA()]
})

Create a manifest.json and add your app icons in public/.

Update environment variables

CRA uses the REACT_APP_ prefix, but Vite uses VITE_.

So this:

REACT_APP_API_URL=https://example.com

Becomes:

VITE_API_URL=https://example.com

In your code, use:

import.meta.env.VITE_API_URL

Package scripts

Update your package.json scripts:

"scripts": {
  "dev": "vite",
  "build": "vite build",
  "preview": "vite preview",
  "test": "vitest",
  "lint": "eslint ./src"
}

Migrate to TypeScript

If your CRA project was using TypeScript, migrating it to Vite is straightforward. Vite supports TypeScript out of the box without the need for Babel or a custom config.

Install required types

You'll need to add these dev dependencies:

npm install --save-dev typescript @types/react @types/react-dom

CRA already generated a tsconfig.json, but if you're starting fresh, you can let Vite scaffold one:

npx vite --template react-ts

Or manually create a tsconfig.json with:

{
  "compilerOptions": {
    "target": "ESNext",
    "module": "ESNext",
    "jsx": "react-jsx",
    "moduleResolution": "Node",
    "strict": true,
    "esModuleInterop": true,
    "skipLibCheck": true,
    "forceConsistentCasingInFileNames": true
  },
  "include": ["src"]
}

Rename files to .tsx

Make sure your entry file is main.tsx instead of main.jsx, and rename any other .js or .jsx files that contain JSX to .tsx.

Update vite.config.js to recognize .ts files if needed (though the default is fine for most setups).

Deployment

After building your app (npm run build), you'll get a dist/ folder. This can be deployed on Netlify, Vercel, Cloudflare Pages, or any static hosting service.

Migrating from CRA to Vite takes a few steps, but it gives you a faster, more modern React setup with better flexibility. From routing and state to testing and styling, Vite makes modern frontend development easier and faster.

Stay Updated

This site is protected by reCAPTCHA and the GooglePrivacy Policy andTerms of Service apply.