Watch the replay of our CI/CD Express Conference 2023 →

    12 Sep 2023 · Software Engineering

    4 Reasons Why You Should Prefer Vite Over Create-React-App (CRA)

    10 min read

    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.

    Source: ViteJs doc

    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.

    1. 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.
    1. 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.
    Source: ViteJs doc

    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.

    Source: ViteJs doc

    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

    1. Remove the react-scripts dependency from the package.json.
    2. Add sass in package.json, if you are using CSS or SCSS.
    yarn add -D sass
    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.

    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
    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
    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 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.

    1. Different tools are being used for bundling development(esbuild) and production code(rollup). This might cause bugs that would be hard to find/fix.
    2. 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.

    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:

    1. Replit – ditched CRA for react starter templates to Vite to give a better experience to their users.
    2. Cypress – is integrating Vite into their products.
    3. Storybook – starting Storybook 6.3–Vite can be used to build storybook.
    4. 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.
    5. Astro – the Astro team reworked their engine to use Vite. It ditched Snowpack for Vite.
    6. Glitch – is using Vite for their starter projects.

    Hoping this article gives you enough reasons to switch to Vite!


    Leave a Reply

    Your email address will not be published. Required fields are marked *

    Writen by:
    I am Chetan, a Senior Software Engineer, I work mostly in the JavaScript ecosystem. I have also contributed to Stack Overflow and love writing technical blogs.
    Avatar for Chetan Gawai
    Reviewed by:
    I picked up most of my soft/hardware troubleshooting skills in the US Army. A decade of Java development drove me to operations, scaling infrastructure to cope with the thundering herd. Engineering coach and CTO of Teleclinic.