How to Transition from CRA to Vite: A Step-by-Step Guide

CRA has been one of my favorite tool. It gave us superpower to spin up react app with out of the box support for eslint , postcss , testing library,Web vitals and many more..

Since CRA has been depreciated and now React team recommend using frameworks like next.js, remix…. which are not only huge, but they have framework specific features.

But these are not necessary for small projects that we already built. Or we just want the same flavour as CRA gave as. Why not migrate to Vite.

As you already know about Vite. if not then, Vite is a modern front-end build tool that offers fast development and optimized production builds.

CRA used to depend upon webpack for its build task and all, but it has huge configuration under the hood.

Migration steps

These migration steps assume that we have a CRA project with Typescript.

  1. Removing CRA dependencies

  2. Installing Vite and its dependencies

  3. Moving index.html

  4. Adding vite.config.ts

  5. Replacing react scripts to Vite scripts

  6. Fixing the env

Optional steps

  1. Configuring eslint

  2. Setting up tailwind for Vite

1. Removing the CRA dependencies

First step includes removing all the CRA dependencies which is surprisingly only react-scripts but behind the scenes it has a lot of dependencies. Now, it feels like react-scripts is bloaty. But it is the thing that made React this much popular. It made react apps do the things which were easily not available to setup from scratch.

npm rm react-scripts

But for now, goodbye react-scripts. we will miss you. JSX has never been evolved if you were not there.

2. Installing Vite and its dependencies

Now moving towards the second step, which is letting the damn thing in. Installing the Vite. Vite will be installed as dev dependency since it is only required for building and development purpose.

npm install vite -D

Only Vite is not enough. Since Vite is bundler, which is not specifically for react, so we need to install its additional dependencies for React app.

  1. @vitejs/plugin-react-swc – A faster, Rust-based plugin for handling React (recommended over the older Babel-based @vitejs/plugin-react).

  2. vite-tsconfig-paths – Helps Vite resolve TypeScript path aliases.

npm install @vitejs/plugin-react-swc vite-tsconfig-paths

Note: Depending on your specific needs, you can choose a different plugin from the official Vite plugins documentation.

3. Moving index.html

Index.html file is not same in CRA and Vite.

In a Create React App (CRA) project, the index.html file is located inside the public/ folder. It serves as the main HTML template where the React app is injected.

Key Points:

  • CRA does not use this file for JavaScript imports; instead, React is injected via index.js or index.tsx.

  • %PUBLIC_URL% is a placeholder that gets replaced with the actual public URL at build time.

So, move index.html file in public/ folder to the root of your project.

And in Vite’s index.html is directly used as the entry point and includes the script reference manually!

<!-- index.html -->
<body>
  .................
  .................

  <!--    Add this script tag before closing body tag -->
  <script type="module" src="/src/index.tsx"></script>
</body>

And replace %PUBLIC_URL% with / or better use dynamic path to ensure that the assets are referenced correctly in Vite, as Vite does not process %PUBLIC_URL% like CRA does.

  • CRA uses %PUBLIC_URL% to dynamically inject the base URL, useful when deploying to subdirectories.

  • Vite doesn't need %PUBLIC_URL%; it serves static assets directly from /public/.

    <link rel="icon" href="%PUBLIC_URL%/favicon.ico" />
    <link rel="apple-touch-icon" href="%PUBLIC_URL%/logo192.png" />
    <link rel="manifest" href="%PUBLIC_URL%/manifest.json" />

    <!-- Change like this -->
    ......
    <link rel="manifest" href="/manifest.json" /> 

    <!-- Or better like this -->
    ........
    <link rel="manifest" href="manifest.json" />/>

4. Adding vite.config.ts

create a vite.config.ts file in root folder of your project and configure for React app using @vitejs/plugin-react-swc plugin.

import { defineConfig } from 'vite'
import react from '@vitejs/plugin-react-swc'

// https://vitejs.dev/config/
export default defineConfig({
  base: '/',
  plugins: [react()]
})

5. Replacing react scripts to Vite scripts

Updating the scripts section in package.json is crucial.


// from this  
"scripts": {
    "start": "react-scripts start",
    "build": "react-scripts build",
    "test": "react-scripts test",
    "eject": "react-scripts eject"
  },

// to this
 "scripts": {
    "start": "vite",
    "build": "tsc && vite build",
    "serve": "vite preview"
}

Ensure your package.json contains:

{
  "type": "module"
}

6. fixing the env

In Vite, environment variables must start with VITE_ to be exposed to the frontend.

Rename Environment Variables

Replace REACT_APP_ prefixes with VITE_ in your .env file:

VITE_API_URL=https://api.example.com
VITE_APP_NAME=My Vite App

Access Environment Variables in Code

Use import.meta.env instead of process.env:

console.log(import.meta.env.VITE_API_URL)
console.log(import.meta.env.VITE_APP_NAME)

Enable TypeScript Support for Vite Env Variables

Create a vite-env.d.ts file inside the src/ directory with the following content:

/// <reference types="vite/client" />

This ensures TypeScript recognizes Vite-specific features like import.meta.env.

Remove Unnecessary React-Specific Files

If your project has a react-app-env.d.ts file inside the src/ directory, delete it, as it's only relevant for Create React App (CRA).

7. Configuring eslint (Optional)

This ESLint setup follows the Vite starter template.

Install ESLint and Required Plugins

Run the following command to install ESLint and necessary dependencies:

npm install eslint globals @eslint/js typescript-eslint eslint-plugin-react-hooks eslint-plugin-react-refresh

Create eslint.config.js

In the root of your project, create a new file named eslint.config.js and add the following configuration:

import js from "@eslint/js";
import globals from "globals";
import reactHooks from "eslint-plugin-react-hooks";
import reactRefresh from "eslint-plugin-react-refresh";
import tseslint from "typescript-eslint";

export default tseslint.config(
  { ignores: ["dist"] },
  {
    extends: [js.configs.recommended, ...tseslint.configs.recommended],
    files: ["**/*.{ts,tsx}"],
    languageOptions: {
      ecmaVersion: 2020,
      globals: globals.browser,
    },
    plugins: {
      "react-hooks": reactHooks,
      "react-refresh": reactRefresh,
    },
    rules: {
      ...reactHooks.configs.recommended.rules,
      "react-refresh/only-export-components": [
        "warn",
        { allowConstantExport: true },
      ],
    },
  }
);

Alternative: Using ESLint Official Setup

If you prefer the official ESLint setup, you can run:

npm init @eslint/config@latest

However, this setup is stricter and may not be ideal for all projects.

8. Setting up tailwind for Vite (Optional)

If you're already using Tailwind CSS, you'll need to reconfigure it. For a fresh installation, refer to the official Tailwind CSS documentation for Vite.

Install Tailwind CSS for Vite

Run the following command to install the required dependency:

npm install @tailwindcss/vite

Configure Tailwind in vite.config.ts

Update your vite.config.ts to include the Tailwind CSS Vite plugin:

import { defineConfig } from 'vite'
import react from '@vitejs/plugin-react-swc'
import tailwindcss from '@tailwindcss/vite'

// https://vitejs.dev/config/
export default defineConfig({
  base: '/',
  plugins: [
    react(),
    tailwindcss(),
  ],
})

This ensures Tailwind CSS is properly integrated into your Vite-powered React app.

0
Subscribe to my newsletter

Read articles from Ahmadullah Mirza directly inside your inbox. Subscribe to the newsletter, and don't miss out.

Written by

Ahmadullah Mirza
Ahmadullah Mirza