Migrating from Create React App to Vite
Create React App has been the default way to start React projects for years. But it can be slow. Running npm start takes time to compile. Making changes requires waiting for rebuilds.
Vite is much faster. It uses native ES modules during development and esbuild for bundling. The difference in speed is noticeable especially on larger projects.
Why migrate
The main reason is development server speed. Vite starts almost instantly. Changes appear in the browser within milliseconds. Create React App can take 30 seconds or more to start and several seconds for hot reload. It's a noticeable difference when you're making frequent changes.
Build times are also faster with Vite. Production builds that took 2-3 minutes with CRA might take 30-40 seconds with Vite.
If your current setup is working fine you might not need to migrate. But if youre frustrated with slow builds Vite is worth trying.
Creating a new Vite project
First lets see what a new Vite project looks like:
npm create vite@latest my-app -- --template react
cd my-app
npm install
npm run dev
The project structure is similar to CRA but with some differences. Vite uses index.html at the root instead of in a public folder.
Migration steps
Start by creating a new Vite project with the same name in a different folder. You'll copy files from your CRA project into this structure.
Update index.html
CRA keeps index.html in public/. Vite keeps it in the root. Move your index.html to the root and update it:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>My App</title>
</head>
<body>
<div id="root"></div>
<script type="module" src="/src/main.jsx"></script>
</body>
</html>
The key change is the script tag. It needs type="module" and points to your entry file.
Rename src/index.js to src/main.jsx
Vite expects the entry file to be main.jsx (or main.tsx for TypeScript). Rename your index file and update the imports:
// src/main.jsx
import React from 'react'
import ReactDOM from 'react-dom/client'
import App from './App'
import './index.css'
ReactDOM.createRoot(document.getElementById('root')).render(
<React.StrictMode>
<App />
</React.StrictMode>
)
This is similar to CRA but uses the new createRoot API.
Update environment variables
CRA uses REACT_APP_ prefix for environment variables. Vite uses VITE_ prefix.
# .env in CRA
REACT_APP_API_URL=https://api.example.com
# .env in Vite
VITE_API_URL=https://api.example.com
Update your code to use the new prefix:
// CRA
const apiUrl = process.env.REACT_APP_API_URL;
// Vite
const apiUrl = import.meta.env.VITE_API_URL;
Handle public assets
CRA serves files from public/ automatically. Vite does the same but the URL handling is different.
In CRA you reference public files with %PUBLIC_URL%:
<img src="%PUBLIC_URL%/logo.png" />
In Vite just use the path:
<img src="/logo.png" />
Update imports
If you use absolute imports with CRA you need to configure them in Vite. Create or update vite.config.js:
import { defineConfig } from 'vite'
import react from '@vitejs/plugin-react'
import path from 'path'
export default defineConfig({
plugins: [react()],
resolve: {
alias: {
'@': path.resolve(__dirname, './src'),
},
},
})
Then update imports in your code:
// Before
import Button from 'components/Button';
// After
import Button from '@/components/Button';
Install dependencies
You need the Vite React plugin:
npm install --save-dev vite @vitejs/plugin-react
Remove CRA dependencies:
npm uninstall react-scripts
Update package.json scripts
Replace CRA scripts with Vite commands:
{
"scripts": {
"dev": "vite",
"build": "vite build",
"preview": "vite preview"
}
}
Common issues
SVG imports
CRA lets you import SVGs as React components with a special syntax. Vite needs a plugin for this:
npm install --save-dev vite-plugin-svgr
// vite.config.js
import svgr from 'vite-plugin-svgr'
export default defineConfig({
plugins: [react(), svgr()],
})
Jest configuration
CRA includes Jest configuration. With Vite you need to set it up yourself. Consider using Vitest instead since it's designed to work with Vite:
npm install --save-dev vitest
CSS Modules
Vite supports CSS Modules out of the box. Files ending in .module.css are treated as CSS Modules just like in CRA.
Global variables
If you use process.env.NODE_ENV you need to change it to import.meta.env.MODE in Vite.
Testing the migration
Run the dev server:
npm run dev
Check that all pages load correctly. Test environment variables, imports, and public assets. Make sure your build still works:
npm run build
npm run preview
The preview command serves the production build locally so you can test it before deploying.
Is it worth it
For small projects the migration might take an hour or two. For large projects with lots of custom configuration it could take longer.
The speed improvement is significant. If slow builds are slowing down your development the migration pays off quickly. You dont want to waste time waiting for hot reload.
If you have a working CRA setup with custom webpack config the migration might be more complex. You'll need to find Vite equivalents for webpack plugins.
Summary
Migrating from CRA to Vite makes development faster. The migration process is straightforward for most projects. Main changes are moving index.html, renaming entry file, and updating environment variable prefixes.
Start with a small project to get familiar with Vite before migrating larger applications. The Vite documentation covers more advanced configuration options.