Migrating From Create React App to Vite (and How I Solved Issues I Faced)
Recently, I had to migrate our web app from Create React App to Vite because CRA is deprecated and the React team is encouraging people to move to other build tools. While I will share the steps to migrate to Vite, I will also share how I solved problems I ran into that did not have clear answers while searching.
Vite is a natural successor to Create React App if you want to keep a single-page app and not switch to frameworks.
Get started, install Vite
npm install -D viteThen, uninstall create-react-app package, which is react-script
npm uninstall react-scriptsUpdate your package.json scripts to use Vite
"scripts": {
"start": "vite",
"build": "vite build",
"serve": "vite preview"
},At the root of your project, create a vite.config.ts file or js. This tells Vite how to run, build, and optimize your project.
import { defineConfig } from 'vite';
export default defineConfig(() => {
return {
plugins: [react()],
};
});Move index.html from the public folder to the project root. Vite treats index.html as part of the module graph.
mv public/index.html .Remove all occurrences of %PUBLIC_URL% in index.html because URLs in index.html are automatically rebased.
Change from
<link rel="icon" href="%PUBLIC_URL%/favicon.ico" />
<link rel="manifest" href="%PUBLIC_URL%/manifest.json" />
to
<link rel="icon" href="/favicon.ico" />
<link rel="manifest" href="/manifest.json" />Finally, link your src/index.tsx file in your index.html file, which you just moved out of the public folder.
<body>
<div id="root"></div>
<script type="module" src="/src/index.tsx"></script>
</body>That should get the app running. But when migrating from an existing CRA project, you may hit some build-time issues. Here are the ones I hit and how I fixed them.
1. Importing SVGs as React components
CRA lets you do this:
import { ReactComponent as HomeIcon } from './home-icon.svgVite needs help to keep that same syntax. I used the SVGR Rollup plugin so the imports keep working the same way.
npm install --save-dev @svgr/rollupupdate your vite.config.ts
import { defineConfig } from 'vite';
import svgr from '@svgr/rollup'
export default defineConfig(() => {
return {
plugins: [react(), svgr()],
};
});This should work for most projects. if you’re like me, where you have different styles of icons and you also find yourself doing this
<HomeIcon className="w-5 h-auto"/>You may notice icons render with odd dimensions. That is because @svgr/rollup uses SVGO by default to optimize SVGs. The optimization may change the view box or other attributes you relied on.
Get Isaac Chukwuka’s stories in your inbox
Join Medium for free to get updates from this writer.
To keep the SVGs exactly as they were, disable SVGO plugins:
import { defineConfig } from 'vite';
import svgr from '@svgr/rollup'
export default defineConfig(() => {
return {
plugins: [
react(),
svgr({
svgoConfig: {
plugins: [],
},
})
],
};
});By passing svgoConfig: { plugins: [] } you tell the plugin to use SVGO but run zero optimization plugins. That restored the icons I had under CRA.
2. Uncaught ReferenceError: process is not defined
Vite exposes env through import.meta.env not process.env. You have two options. Either update your code base to use import.meta.env everywhere, or define process.env in Vite config.
import { defineConfig } from 'vite';
import svgr from '@svgr/rollup'
export default defineConfig(() => {
return {
plugins: [
react(),
svgr({
svgoConfig: {
plugins: [],
},
}),
define: {
'process.env': {}
}
],
};
});3. Absolute Imports might break
If you used absolute imports in CRA, you can get them to work the same way with vite-tsconfig-paths.
npm i vite-tsconfig-pathsUpdate your vite.config.ts file
import { defineConfig } from 'vite';
import svgr from '@svgr/rollup'
import tsconfigPaths from 'vite-tsconfig-paths'
export default defineConfig(() => {
return {
plugins: [
react(),
svgr({
svgoConfig: {
plugins: [],
},
}),
tsconfigPaths()
define: {
'process.env': {}
}
],
};
});And in your tsconfig.json file, add
"baseUrl": "src"3. Keep localhost port 3000
If you want to keep the familiar localhost:3000, set the server port in vite.config.ts:
import { defineConfig } from 'vite';
import svgr from '@svgr/rollup'
import tsconfigPaths from 'vite-tsconfig-paths'
export default defineConfig(() => {
return {
plugins: [
react(),
svgr({
svgoConfig: {
plugins: [],
},
}),
tsconfigPaths()
define: {
'process.env': {}
}
],
server: {
port: 3000,
},
};
});4. Deploying to production and don’t want to change your build directory
If you want Vite to output a build like CRA did, set outDir in build
import { defineConfig } from 'vite';
import svgr from '@svgr/rollup'
import tsconfigPaths from 'vite-tsconfig-paths'
export default defineConfig(() => {
return {
plugins: [
react(),
svgr({
svgoConfig: {
plugins: [],
},
}),
tsconfigPaths()
define: {
'process.env': {}
}
],
server: {
port: 3000,
},
build: {
outDir: 'build',
},
};
});That wraps my migration journey and the issues I ran into. I hope this helps. If you run into any of these issues, let me know, and I will be happy to help and answer any questions.




