Migrating from Create-React-App to Vite: Simple Tutorial

Jan ŠkorubaJan Škoruba
4 min read

How to Migrate from Create React App to Vite

I recommend installing a new project with the Vite CLI and copying your code there. This way, you get the latest version of Vite configs, including tsconfig, etc.

  • What I am using in the project:
    • Sass
    • SVG files
    • Environment variables with the prefix REACT_APP
      • Also uses environment variables in index.html

Step 1: Set Up a New Vite Project

  1. Install the Vite CLI:
npm install -g create-vite
  1. Create a Vite project:
create-vite my-react-app --template react-ts
  1. Install dependencies:
npm install

Step 2: Copy Your Existing CRA Project Files

  1. Copy files:

    Copy the src and public directories from your CRA project to your new Vite project.

  2. Install additional dependencies:

    Update your package.json dependencies from your CRA project in the new project:

npm install
  1. Update typescript to the latest version.
npm install typescript@latest
  1. Uninstall react-scripts:
npm uninstall react-scripts

Step 3: Configure Vite

In my case, I want to keep all environment variables with the prefix REACT_APP. I configured it as follows:

  1. Configure vite.config.ts:

    In my case, I use the following additional configuration for:

    • Sass
      • npm i vite-plugin-sass-dts
      • npm i sass
    • SVG files
      • npm i vite-plugin-svgr
    • Keep environment variables with prefix REACT_APP
      • npm i vite-plugin-env-compatible
    • Populate environment variables in index.html
      • npm i vite-plugin-html

    Modify vite.config.ts to handle environment variables, SVG files, and Sass imports.

    I created two files for env and plugins:

    • To use these files, extend tsconfig.node.json:
-"include": ["vite.config.ts"]
+"include": ["vite.config.ts", "vite.plugins.ts", "vite.env.ts"]

vite.env.ts:

import dotenv from "dotenv";
import dotenvExpand from "dotenv-expand";

// Load .env files
const env = dotenv.config();
dotenvExpand.expand(env);

export const REACT_APP_PREFIX = "REACT_APP_";
export const VITE_PREFIX = "VITE_";

export const getEnvVariables = () => {
  const envVariables = Object.keys(process.env).reduce(
    (prev: Record<string, any>, key) => {
      if (key.startsWith(REACT_APP_PREFIX) || key.startsWith(VITE_PREFIX)) {
        prev[key] = process.env[key];
      }
      return prev;
    },
    {}
  );
  return envVariables;
};

export const envVariables = getEnvVariables();
export const VITE_PUBLIC_URL = envVariables.VITE_PUBLIC_URL;

vite.plugins.ts:

import react from "@vitejs/plugin-react";
import svgr from "vite-plugin-svgr";
import { createHtmlPlugin } from "vite-plugin-html";
import viteSassDts from "vite-plugin-sass-dts";
import env from "vite-plugin-env-compatible";
import { envVariables, REACT_APP_PREFIX } from "./vite.env";

export const configurePlugins = () => {
  return [
    react(),
    svgr({
      svgrOptions: {
        exportType: "named",
        ref: true,
        svgo: false,
        titleProp: true,
      },
      include: "**/*.svg",
    }),
    viteSassDts(),
    createHtmlPlugin({
      inject: {
        data: {
          ...envVariables,
        },
      },
    }),
    env({
      prefix: REACT_APP_PREFIX,
    }),
  ];
};

vite.config.ts:

import { defineConfig } from "vite";
import { VITE_PUBLIC_URL } from "./vite.env";
import { configurePlugins } from "./vite.plugins";
import path from "path";

export default defineConfig({
  base: VITE_PUBLIC_URL,
  plugins: configurePlugins(),
  resolve: {
    alias: {
      "@": path.resolve(__dirname, "./src"),
    },
  },
  server: {
    port: 3000,
  },
  build: {
    outDir: "build",
    emptyOutDir: true,
  },
});
  1. Install Vite plugins:
npm install @vitejs/plugin-react dotenv dotenv-expand vite-plugin-svgr vite-plugin-html vite-plugin-sass-dts vite-plugin-env-compatible
  1. Optional: Migrate to VITE instead of REACT_APP variables:

    File .env:

VITE_MY_VARIABLE=Test

File App.tsx:

- process.env.VITE_MY_VARIABLE
+ import.meta.env.VITE_MY_VARIABLE

SVG Files

  • In your code, you can use the same approach as in CRA:
import { ReactComponent as MyIcon } from "./images/MyIcon.svg";

index.html

  • Index.html - now located in the root folder (previously in the public folder)
<html lang="en">
<head>
<meta charset="utf-8" />

+<base href="<%= VITE_PUBLIC_URL %>" />
-<base href="%PUBLIC_URL%/" />

<meta name="viewport" content="width=device-width, initial-scale=1" />
<meta name="description" content="App Name" />

<title>App Name</title>
</head>
<body>
<noscript>You need to enable JavaScript to run this app.</noscript>
<div id="root"></div>
+<script type="module" src="/src/index.tsx"></script>
</body>
</html>

PUBLIC_URL Variable

  • PUBLIC_URL was originally used in the router, so it is necessary to change it:
-<BrowserRouter basename={process.env.PUBLIC_URL}>
+<BrowserRouter basename={import.meta.env.VITE_PUBLIC_URL}>
 <App />
 </BrowserRouter>

File .env.development:

VITE_PUBLIC_URL=/

File .env.production:

VITE_PUBLIC_URL=/my-app

Step 4: Update package.json Scripts Section

"scripts": {
-"start": "react-scripts start",
+"start": "vite",
-"build": "react-scripts build",
+"build": "vite build",
-"eject": "react-scripts eject",
+"preview": "vite preview"
}

Step 5: Run It 🚀

npm run start

Feel free to share your feedback and suggestions for improvements!

Thanks for reading. 👋🏻

0
Subscribe to my newsletter

Read articles from Jan Škoruba directly inside your inbox. Subscribe to the newsletter, and don't miss out.

Written by

Jan Škoruba
Jan Škoruba

I am a Software Developer and Identity Consultant, specializing in securing applications and APIs using OpenID Connect and OAuth.