Migrate Create React App to Vite


This post goes over how to migrate from Create React App (CRA) to Vite since the former is deprecated.

package.json

Uninstall react-scripts:

npm uninstall react-scripts

Install vite:

npm install --save-dev vite @vitejs/plugin-react-swc

Update npm “scripts”:

 {
   "scripts": {
-    "build": "react-scripts build",
+    "build": "vite build",
-    "eject": "react-scripts eject",
-    "start": "react-scripts start",
+    "start": "vite --open",
-    "test": "react-scripts test"
+    "test": "jest --watch"
   }
 }

Environment Variables

Replace process.env.REACT_APP with import.meta.env.VITE_APP:

git grep -l 'process.env.REACT_APP' | xargs sed -i '' -e 's/process.env.REACT_APP/import.meta.env.VITE_APP/g'

Then replace REACT_APP with VITE_APP in .env:

git grep -l REACT_APP | xargs sed -i '' -e 's/REACT_APP/VITE_APP/g'

Replace process.env.NODE_ENV checks with import.meta.env.DEV. For example:

-if (process.env.NODE_ENV === 'development') {
+if (import.meta.env.DEV) {

If you’re using TypeScript, create a type declaration file:

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

interface ImportMetaEnv {
  readonly DEV: boolean;
  readonly VITE_APP_NAME: string;
  readonly VITE_APP_VERSION: string;
}

interface ImportMeta {
  readonly env: ImportMetaEnv;
}

It’s good practice to consolidate all your environment variables in src/config/index.js so that you can mock them in your tests with src/config/__mocks__/index.js by adding the mock file to src/setupTests.js:

echo 'jest.mock('./config');' >> src/setupTests.js

This fixes the TypeScript error:

src/config/index.ts:01:20 - error TS1343: The 'import.meta' meta-property is only allowed when the '--module' option is 'es2020', 'es2022', 'esnext', 'system', 'node16', or 'nodenext'.

01 export const DEV = import.meta.env.DEV;
                      ~~~~~~~~~~~

index.html

Move public/index.html to the root:

mv public/index.html .

Remove %PUBLIC_URL% since Vite serves static assets under the public directory:

sed -i '' -e 's/%PUBLIC_URL%//g' index.html

Add the script tag to src/index.js before the closing body tag:

<script type="module" src="src/index.js"></script>

For example:

    <div id="root"></div>
    <script type="module" src="src/index.js"></script>
  </body>
</html>

vite.config.mjs

Create vite.config.mjs:

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

export default defineConfig({
  plugins: [react()],
});

If you want to keep the build directory consistent with CRA:

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

 export default defineConfig({
+  build: {
+    outDir: '../build',
+  },
   plugins: [react()],
 });

Otherwise, Vite will build to dist.

Now run your app:

npm start

If you get the error in your browser console:

require is not defined

Then install vite-plugin-commonjs:

npm install --save-dev vite-plugin-commonjs

And update vite.config.mjs:

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

 export default defineConfig({
   build: {
     outDir: '../build',
   },
-  plugins: [react()],
+  plugins: [react(), commonjs()],
 });

manifest.json

If you see the error in your browser console:

Manifest: property 'start_url' ignored, URL is invalid.

Then replace manifest.json “start_url” with your absolute or relative URL.

For example:

-  "start_url": ".",
+  "start_url": "https://example.com",

If you see the error in your browser console:

Manifest: property 'src' ignored, URL is invalid.

Then replace manifest.json “src” with your absolute or relative URL.

For example:

   "icons": [
     {
-      "src": "favicon.ico",
+      "src": "https://example.com/favicon.ico",
       "sizes": "64x64 32x32 24x24 16x16",
       "type": "image/x-icon"
     },
     {
-      "src": "/android-chrome-192x192.png",
+      "src": "https://example.com/android-chrome-192x192.png",
       "type": "image/png",
       "sizes": "192x192"
     },
     {
-      "src": "/android-chrome-512x512.png",
+      "src": "https://example.com/android-chrome-512x512.png",
       "type": "image/png",
       "sizes": "512x512"
     }
   ],

Jest

Install jest dependencies:

npm install --save-dev jest jest-environment-jsdom

If you’re using TypeScript, install ts-jest dependencies:

npm install --save-dev ts-jest ts-node

Move your Jest config from package.json to jest.config.js.

This is optional but create a test directory:

mkdir -p test

Move src/setupTests.js to the test directory and fix the imports:

mv src/setupTests.js test/setupTests.js

Create test/__mocks__/fileMock.js:

module.exports = 'test-file-stub';

Create test/__mocks__/styleMock.js:

module.exports = {};

Create test/__mocks__/svgMock.js:

module.exports = {
  ReactComponent: () => 'IconMock',
};

Your jest.config.js should look like the following:

/** @type {import('jest').Config} */
const config = {
  collectCoverageFrom: ['<rootDir>/src/**/*.{js,jsx}'],
  coverageThreshold: {
    global: {
      branches: 100,
      functions: 100,
      lines: 100,
      statements: 100,
    },
  },
  moduleNameMapper: {
    '\\.(css|less|sass|scss)$': '<rootDir>/test/__mocks__/styleMock.js',
    '\\.(jpg|jpeg|png|gif|eot|otf|webp|ttf|woff|woff2|mp4|webm|wav|mp3|m4a|aac|oga)$':
      '<rootDir>/test/__mocks__/fileMock.js',
    '\\.svg$': '<rootDir>/test/__mocks__/svgMock.js',
  },
  // preset: 'ts-jest',
  setupFilesAfterEnv: ['<rootDir>/test/setupTests.js'],
  testEnvironment: 'jsdom',
};

module.exports = config;

Fix any failing tests and update snapshots:

npx jest -u

If you’re getting the error:

ReferenceError: Request is not defined

This means you need to polyfill fetch.

ESLint

Install eslint:

npm install --save-dev eslint

Remove react-app from .eslintrc:

 {
-  "extends": ["react-app", "react-app/jest"]
 }

If you’re using TypeScript, install the dependencies:

npm install --save-dev @typescript-eslint/{eslint-plugin,parser}

Then update .eslintrc:

{
  "parser": "@typescript-eslint/parser",
  "extends": ["eslint:recommended", "plugin:@typescript-eslint/recommended"]
}

Run ESLint and fix any errors:

npx eslint .

TypeScript

If you’re getting the error:

error TS2307: Cannot find module './file.png' or its corresponding type declarations.

Then create a type declaration file:

declare module '*.png' {
  const src: string;
  export default src;
}

Port

Replace localhost:3000 with localhost:5173:

git grep -l 'localhost:3000' | xargs sed -i '' -e 's/localhost:3000/localhost:5173/g'

CI

If you’re running a Vite server in CI, then you’ll need to pass the --host option:

npx vite --host

Example



Please support this site and join our Discord!