Create React App (CRA) has long been the go-to tool for most developers to scaffold React projects and set up a dev server. It offers a modern build setup with no configuration.
But, we see increased development and build time when the project size increases. This slow feedback loop affects developers’ productivity and happiness.
To address these issues, there is a new front-end tooling in the ecosystem: Vite!
Unlike CRA, Vite does not build your entire application before serving, instead, it builds the application on demand. It also leverages the power of native ES modules, esbuild, and Rollup to improve development and build time.
Let’s take a look in detail at what can make CRA slow.
Why is CRA slow?
CRA uses a webpack under the hood. The webpack bundles the entire application code before it can be served. With a large codebase, it takes more time to spin up the development server and reflecting the changes made takes a long time.
The diagram below shows how all the code must be bundled in order to start a bundle-based dev server.

What is Vite?
Vite is a next-generation, front-end tool that focuses on speed and performance.
It consists of two major parts:
- A development server that provides rich feature enhancements over native ES modules: fast Hot Module Replacement (HMR), pre-bundling, support for typescript, jsx, and dynamic import.
- A build command that bundles your code with Rollup, pre-configured to output optimized static assets for production.
Why prefer Vite over create-react-app?
1. Faster spin-up of the development server
Unlike a CRA or bundler-based build setup, Vite does not build the entire application before serving. It divides the application modules into two categories: dependencies and source code.
- Dependencies are plain JavaScript that do not change often during development (large component libraries like mui).
- Vite pre-bundles these dependencies using esbuild, which is 10-100x faster than JavaScript-based bundlers.
- Pre-bundling ensures that each dependency maps to only one HTTP request, avoiding HTTP overhead and network congestion.
- As dependencies do not change, they can also be cached and we can skip pre-bundling.
- Source code often contains non-plain JavaScript that needs transforming (e.g. JSX, CSS) and will be edited very often.
Vite serves source code over native ESM.
What is native ESM? How does it improve the dev server start time?
ESM stands for EMACScript modules. It is a recent addition to the JavaScript language specification that deals with how modules are loaded in the Javascript application.
Benefits of building a dev server around native ESM
- There is no need for bundling. That’s a big chunk of work that you donβt have to do anymore.
- Native EMS is on-demand by nature. Based on the browser request, Vite transforms and serves source code on demand. If the module is not needed for some screen, it is not processed. The diagram shows how the native ESM-based dev server starts with only the required modules loaded.

2. Less waiting time for reflecting file updates
As the size of the codebase increases, file updates get slower in CRA. This is not the case with Vite.
Let’s see how Vite handles file updates.
- In Vite, Hot Module Replacement(HMR) is performed over native ESM. When a file is edited, Vite invalidates the chain between the edited module and its closest HMR boundary. This makes HMR updates simple and fast regardless of the size of your application.
- Modules are fetched over HTTP requests. Vite leverages HTTP headers for caching to speed up full-page reloads. Source code module requests are made conditional via 304 Not Modified. And dependency module requests are cached via Cache-Control: max-age=31536000, immutable.
3. Improved build performance
Vite ships with a pre-configured build command that bakes in many performance optimizations out of the box.
1. Async chunk loading optimization
While code splitting, Webpack, and Rollup produce a common chunk (code that is shared between two or more other chunks). This, when combined with dynamic import, can lead to many network round trips.

As shown in the image in unoptimized scenarios, when async chunk A is imported, the browser does not know that it needs common chunk C without first requesting and parsing A. After figuring out that it needs common chunk C, the browser then imports it, leading to an extra network round trip.
Entry ---> A ---> C
Whereas, Vite rewrites code-split dynamic import calls with a preload step. When A is requested, C is fetched in parallel. This eliminates network round trips.
Entry ---> (A + C)
2. CSS code splitting
Vite automatically extracts the CSS used by modules in an async chunk and generates a separate file for it. The CSS file is automatically loaded via a <link> tag when the associated async chunk is loaded.
4. Rich features
Vite provides out-of-the-box support for TypeScript, JSX, CSS, and more.
- TypeScript
Vite supports importing `.ts` files out of the box. It uses esbuild to transpile TypeScript into JavaScript which is about 20~30x faster than vanilla tsc. The HMR updates can reflect in the browser in under 50ms.
- JSX
Viet supports `.jsx` and `.tsx` files out of the box. JSX transpilation is handled via esbuild.
- CSS
Importing `.css` files injects content to the page via a` <style>` tag with HMR support.
Vite provides support for .scss, .sass,.less, .styl and .stylus files, PostCSS, and CSS. modules.
- JSON
JSON files can be directly imported into Vite. It also supports named imports.
- Dynamic Imports
Dynamic import with variables can be used with Vite.
Considering the aforementioned benefits of Vite, I decided to migrate an existing project from CRA to Vite. Letβs take a look at how I did it.
Steps to migrate CRA to Vite
- Remove the
react-scripts
dependency from the package.json. - Add
sass
in package.json, if you are using CSS or SCSS.
yarn add -D sass
//or
npm i --save-dev sass
3. Add vite
and @vitejs/plugin-react
as dev dependencies.
"devDependencies": {
"@vitejs/plugin-react": "1.3.2",
"vite": "2.7.0"
}
4. Replace the start
and build
scripts as below:
"scripts": {
"start": "vite",
"build": "vite build"
},
5. Create a file vite.config.js
in the root of the project with the below code:
import { defineConfig } from "vite";
import react from "@vitejs/plugin-react";
export default () => {
return defineConfig({
plugins: [react()],
})
}
6. Move the index.html file from the public folder to the root of the project.
7. Remove all the occurrences of %PUBLIC_URL%
from index.html.
The lines affected should look like the following lines after the change:
<link rel="icon" href="/favicon.ico" />
<link rel="manifest" href="/manifest.json" />
<link rel="apple-touch-icon" href="/logo192.png" />
8. Add the script tag below into the body of index.html:
<script type="module" src="/src/index.jsx"></script>
NOTE: In CRA, src/index
has extension of .js
, change it to jsx
.
9. Change the extension of .js
files to .jsx
, if they contain jsx syntax.
If not changed, you will get the following error:
[plugin:vite:import-analysis] Failed to parse source for import analysis because the content contains invalid JS syntax. If you are using JSX, make sure to name the file with the .jsx or .tsx extension.
Want to know why this change is needed?
Read through Evan You’s tweet explaining why components containing jsx syntax should have a .jsx
extension.
10. Update all environment variables from REACT_APP
to VITE
.
Replace REACT_APP_HOST_URL
to VITE_HOST_URL
11. Vite does not support absolute path by default.
You need to either use a relative path or add an alias.
import App from "components/search/App"; // Not supported
import App from '../src/components/search/App'; // Use relative path
You can add an alias in vite.config.js as below:
export default () => {
return defineConfig({
plugins: [react()],
resolve: {
alias: {
'@': path.resolve(__dirname, './src'),
},
},
});
}
Now, you can use the next line:
import App from '@/components/search/App'
Finally installing dependencies using:
yarn install or npm install
12. Start your application with, as shown below:
yarn start
or
npm start
Voila! Your application has migrated from CRA to Vite! Pretty smooth.
I saw significant improvement in the build time after migration.
Build time using CRA:

Build time using Vite:

Creating new applications with Vite
Now, you know the benefits of using Vite. Let’s create a new application using Vite.
yarn create vite
or
npm create vite@latest
Follow the prompts specifying the project name and template.

You can also use a single command specifying the project name and template.
// npm 6.x
npm create vite@latest my-react-app --template react
//npm 7+, extra double-dash is needed
npm create vite@latest my-react-app -- --template react
//yarn
yarn create vite my-react-app --template react
Some important points for consideration for switching to Vite
Webpack has been ruling the ecosystem for a long time now. The CRA/webpack community is very rich in loaders and plugins compared to the Vite community.
Due to the popularity of webpack and the reasons listed below, some users are quite skeptical about using Vite over webpack.
- Different tools are being used for bundling development(
esbuild
) and production code(rollup
). This might cause bugs that would be hard to find/fix. - Because Vite is new, users are not sure how it will handle large and complex use cases.
However, Vite is growing rapidly and trying to address issues quickly.
- It has 1,557,301 weekly downloads as compared to 142,299 CRA weekly downloads.
- Vite is actively maintained. Whereas issues in CRA are addressed slowly as the maintainers of CRA are working in their free time.
- Vite might consider switching to esbuild for production build when important features in
esbuild
are stabilized. This will solve the first problem mentioned above.
Many industries have recognized Vite’s potential and started using or switching to Vite. Below is a list of companies that are using and even sponsoring Vite:
- Replit – ditched CRA for react starter templates to Vite to give a better experience to their users.
- Cypress – is integrating Vite into their products.
- Storybook – starting Storybook 6.3βVite can be used to build storybook.
- Tailwind – tailwind Labs was one of the first teams to recognize Vite’s potential and started to sponsor the project. It released Just-in-Time Mode providing a great experience when paired with Vite HMR.
- Astro – the Astro team reworked their engine to use Vite. It ditched Snowpack for Vite.
- Glitch – is using Vite for their starter projects.
Hoping this article gives you enough reasons to switch to Vite!
References:
Thank you. This was immensely helpful in me deciding whether to use CRA or Vite for my new project. I decided on Vite and I am quite happy with the performance so far.
Why is Vite so buggy?