Migrating from create-react-app to vite
Reason for migration
create-react-app
production builds were slow, integration with tailwind was limited, and the future of create-react-app
is unclear.
The following steps were built via trial and error. Good luck!
Required Packages
-
Remove
create-react-app
npm uninstall react-scripts
-
Install
vite
npm install -D vite @vitejs/plugin-react
-
If you test locally with ssl
npm install -D @vitejs/plugin-basic-ssl
NodeJS polyfills
- If you have dependency errors you may be missing NodeJS polyfills
create-react-app
versions < 5 polyfill NodeJS APIsvite
does not include NodeJS polyfillsnpm install node-stdlib-browser
- Inject references to the Node APIs used by your application in
vite.config.js
- An example is at the end of this article.
Environment variables
Change all environment variable prefixes from REACT_APP
to VITE
. For example, REACT_APP_KEY
would be VITE_KEY
. Here’s a way to rename them with Sublime Text.
- Open empty Sublime Text window
- Added project folder
- Open
Find in Files
dialog - In the bottom bar that says
Where:
click the...
button on the right - Select Add Open Folders.
- In
Find:
writeREACT_APP_
- In
Replace:
writeVITE_
- Click replace
- Select Save All.
process.env
references must also be replaced with import.meta.env
. Repeat the above steps to find and replace those.
There are better ways to do this. Send a script and I might add it here :)
All files with JSX in them must have .jsx file extension
Why Vite requires .jsx
file extensions is described here.
You can use this gist (MacOS / Linux only) to rename your files. It searches through ./src
and renames all .js
files with JSX
markup in them to use the .jsx
extension instead.
find ./src -type f -name '*.js' -not -name '*.jsx' -not -name '*.ejs' -exec bash -c 'grep -l "</" $0' {} \; -exec bash -c 'mv "$0" "${0%.js}.jsx"' {} \;
find ./src -type f -name '*.js' -not -name '*.jsx' -not -name '*.ejs' -exec bash -c 'grep -l "/>" $0' {} \; -exec bash -c 'mv "$0" "${0%.js}.jsx"' {} \;
There are two because one finds files with </
in them and the other with />
in them.
File locations
- Move
index.html
fromsrc
folder to your base folder. - Add
<script type="module" src="/src/index.jsx"></script>
under the root tag.
<!DOCTYPE html>
<html lang='en'>
<!-- ... head code here -->
<body>
<div id='root'></div>
<script type="module" src="/src/index.jsx"></script>
</body>
</html>
Render initial component into the root div and remove the serviceWorker
code added by create-react-app
.
src/index.jsx
example
import App from './App';
import { createRoot } from 'react-dom/client';
import './css/index.css';
const container = document.getElementById('root');
const root = createRoot(container);
root.render(<App tab="home" />);
Node scripts
- Rename every instance of
react-scripts
inpackage.json
tovite
. - Add a
preview
script that runsvite preview
. "preview": "vite preview"
HTTP proxy
npm uninstall http-proxy-middleware
- Remove
src/setupProxy.js
if you created one forcreate-react-app
Here’s our old setupProxy.js
for reference:
const createProxyMiddleware = require('http-proxy-middleware');
module.exports = function(app) {
app.use(
'/api',
createProxyMiddleware({
target: 'https://localhost:1111',
changeOrigin: true,
secure: false
})
);
app.use(
'/sso',
createProxyMiddleware({
target: 'https://localhost:1112',
changeOrigin: true,
secure: false
})
);
};
This will be replaced by the proxy section in vite.config.js
.
HTTP proxy error
If you receive the following error while using the http proxy:
[vite] http proxy error:
Error: read ECONNRESET
at TLSWrap.onStreamRead (node:internal/stream_base_commons:217:20)
Set the http agent manually. This may be a bug in the node-http-proxy
library.
This was an odd issue because it only showed up on newer versions of node.
Production builds
create-react-app
outputs production builds tomap/build
- You can edit your config’s
outDir
to bebuild
if you have scripts that requirecreate-react-app
’s original location - Test production builds by running
vite build
and thenvite preview
- Try your app in both
dev
andpreview
mode because behavior can differ - Check for errors in the console
- You’ll get better error messages if you turn off minification.
build: {
minify: false,
}
- Imports that work while using the dev server may not work in production.
import * as MapboxDraw from '@mapbox/mapbox-gl-draw';
worked in the dev server but not withvite preview
import MapboxDraw from '@mapbox/mapbox-gl-draw';
worked in both.var turf = require('@turf/turf');
did not work and had to be changed toimport * as turf from '@turf/turf';
CSS loading order
After switching to vite
we had issues with imported CSS.
- CSS imports in
create-react-app
loaded in a different order thanvite
- Our application CSS was loading before external library CSS with
vite
create-react-app
was doing the oppositedev
andpreview
mode also sometimes behave differently
There’s some discussion about loading CSS in vite here:
- https://github.com/vitejs/vite/pull/9278
- https://github.com/vitejs/vite/pull/9949
- https://github.com/vitejs/vite/issues/3924
If you have issues here’s how to check the order:
Development
npm start
(run vite in dev mode)- Open up dev tools
- Select Elements (Chrome) / Inspector (Firefox)
- Expand the
<head>
tag - Look at the
<style>
tags inside the<head>
tag - There will be multiple inline style tags that map to your import statements
- Check order of CSS
Production
npm run build
npm run preview
(run vite in preview mode)- Look at dev tools
- Instead of multiple
<style>
tags, there will be a link to a style sheet <link rel="stylesheet" href="/assets/index.017f52c7.css">
- Right click the link and “Open in New Tab” (Chrome) / “Open File in Style Editor” (Firefox)
- The style sheet has the style sheets you saw in
dev
combined into a single file - Check order of the CSS and compare with
dev
if they behave differently
We moved some of our CSS import statements out of our components and up to the initial App
component. We’re not sure why that fixed things unfortunately!
Example vite.config.js
- Edit this config to fit your project
- Demonstrates two backend services being proxied
- If you don’t need a proxy
- Remove the
proxy
block(s)
- Remove the
- If you’re ok with vite’s default build folder
- Remove the
build
block
- Remove the
- If you don’t need ssl
- Remove
https
import - Remove
https: true
- Remove the
basicSsl()
import statement and call - Remove everything referencing
agent
- Remove
import { defineConfig } from 'vite'
import react from '@vitejs/plugin-react'
import basicSsl from '@vitejs/plugin-basic-ssl'
import stdLibBrowser from 'node-stdlib-browser'
import inject from '@rollup/plugin-inject'
import https from 'https'
const agent = new https.Agent({keepAlive: true})
// https://vitejs.dev/config/
export default defineConfig({
server: {
port: 3000,
https: true,
proxy: {
'/api': {
target: 'https://localhost:1111',
changeOrigin: true,
secure: false,
agent
},
'/sso': {
target: 'https://localhost:1112',
changeOrigin: true,
secure: false,
agent
}
}
},
preview: {
port: 3000,
https: true,
proxy: {
'/api': {
target: 'https://localhost:1111',
changeOrigin: true,
secure: false,
agent
},
'/sso': {
target: 'https://localhost:1112',
changeOrigin: true,
secure: false,
agent
}
}
},
build: {
minify: false,
outDir: 'build'
},
optimizeDeps: {
include: ['buffer', 'process']
},
plugins: [
react({ fastRefresh: false }),
basicSsl(),
{
...inject({
global: [
require.resolve(
'node-stdlib-browser/helpers/esbuild/shim'
),
'global'
],
process: [
require.resolve(
'node-stdlib-browser/helpers/esbuild/shim'
),
'process'
],
Buffer: [
require.resolve(
'node-stdlib-browser/helpers/esbuild/shim'
),
'Buffer'
]
}),
enforce: 'post'
}
]
})
Hope this helped!