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-appnpm uninstall react-scripts
-
Install
vitenpm 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-appversions < 5 polyfill NodeJS APIsvitedoes 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 Filesdialog - 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.htmlfromsrcfolder 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-scriptsinpackage.jsontovite. - Add a
previewscript that runsvite preview. "preview": "vite preview"
HTTP proxy
npm uninstall http-proxy-middleware- Remove
src/setupProxy.jsif 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-appoutputs production builds tomap/build- You can edit your config’s
outDirto bebuildif you have scripts that requirecreate-react-app’s original location - Test production builds by running
vite buildand thenvite preview - Try your app in both
devandpreviewmode 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 previewimport 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-apploaded in a different order thanvite - Our application CSS was loading before external library CSS with
vite create-react-appwas doing the oppositedevandpreviewmode 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 buildnpm 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
devcombined into a single file - Check order of the CSS and compare with
devif 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
proxyblock(s)
- Remove the
- If you’re ok with vite’s default build folder
- Remove the
buildblock
- Remove the
- If you don’t need ssl
- Remove
httpsimport - 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!