DEV Community

Sahil Kumar
Sahil Kumar

Posted on • Edited on

2 1 1 1 1

Migrating from Create React App to Vite — A Step-by-Step Guide

Migrating from Create React App (CRA) to Vite enhances development speed and efficiency, making it ideal for industry-level projects. CRA’s reliance on Webpack often results in slower builds and hot module replacement (HMR). Vite, leveraging ES modules, provides instant HMR, faster builds, and a leaner setup. This guide walks through the migration process, covering essential updates for configurations, environment variables, TypeScript, testing, and build optimizations.

1. Install Required Packages

First, install the necessary dependencies for Vite:

npm install vite @vitejs/plugin-react --save-dev
Enter fullscreen mode Exit fullscreen mode
  • vite is the core build tool and development server that provides fast bundling and HMR.
  • @vitejs/plugin-react enables React-specific optimizations like Fast Refresh and JSX transformation.

2. Create Vite Configuration File

Create a vite.config.ts file in the root directory:

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

export default defineConfig({
  plugins: [react()],
  server: {
    port: 3000,
    open: true,
  },
});
Enter fullscreen mode Exit fullscreen mode

  • server.port specifies the port on which the Vite development server will run. By default, Vite uses port 5173, by setting it to 3000 makes it consistent with CRA.
  • server.open automatically opens the browser when the development server starts.

3. Move index.html to Root

Move public/index.html to the project root and update src:

<script type="module" src="./src/index.jsx"></script>
Enter fullscreen mode Exit fullscreen mode

Unlike Webpack, which dynamically injects the src attribute into index.html using plugins like HtmlWebpackPlugin, Vite requires you to explicitly define the entry file. Since Vite does not modify index.html dynamically, specifying the src ensures the application loads correctly.

Now, remove all %PUBLIC_URL% occurrences in the index.html file:

<!-- CRA -->
<link rel="icon" href="%PUBLIC_URL%/favicon.ico" />

<!-- VITE -->
<link rel="icon" href="/favicon.ico" />
Enter fullscreen mode Exit fullscreen mode

4. Update Script Commands

Replace the CRA scripts in package.json with Vite equivalents:

"scripts": {
  "start": "vite",
  "build": "vite build",
}
Enter fullscreen mode Exit fullscreen mode

5. Update Environment Variables

Vite exposes env variables under import.meta.env object as strings automatically. To prevent accidentally leaking env variables to the client, only variables prefixed with VITE_ are exposed to your Vite-processed code.

Update Variable Declarations:

Replace REACT_APP_ with VITE_ in all your environment variables

// CRA
REACT_APP_STAGING = staging;

// VITE
VITE_STAGING = staging;
Enter fullscreen mode Exit fullscreen mode
Update Variable Usage:
// CRA
export const config = process.env.REACT_APP_STAGING ? "staging" : "dev";

// VITE
export const config = import.meta.env.VITE_STAGING ? "staging" : "dev";
Enter fullscreen mode Exit fullscreen mode

6. Improve TypeScript Support and tsconfig.json

If your project uses TypeScript, follow these steps to ensure compatibility and improved performance.

Update the following properties in tsconfig.json:

{
  "compilerOptions": {
    "moduleResolution": "bundler",
    "isolatedModules": true,
    "types": ["vite/client"]
  },
  "include": ["vite.config.ts"]
}
Enter fullscreen mode Exit fullscreen mode

One thing you should know about Vite is that Vite only performs transpilation on .ts files and does NOT perform type checking. It assumes type checking is taken care of by your IDE and build process.

The reason Vite does not perform type checking as part of the transform process is because the two jobs work fundamentally differently. Transpilation can work on a per-file basis and aligns perfectly with Vite's on-demand compile model. In comparison, type checking requires knowledge of the entire module graph. Shoe-horning type checking into Vite's transform pipeline will inevitably compromise Vite's speed benefits.

Adding type checking support in Development

During development, if you need more than IDE hints, Vite recommends running tsc --noEmit --watch in a separate process, or use vite-plugin-checker if you prefer having type errors directly reported in the browser. In this guide we will go with the second approach.

Install vite-plugin-checker:

npm install --save-dev vite-plugin-checker
Enter fullscreen mode Exit fullscreen mode

Update vite.config.ts:

import tsChecker from "vite-plugin-checker";

export default defineConfig({
  plugins: [tsChecker({ typescript: true })],
});
Enter fullscreen mode Exit fullscreen mode
Adding type checking support for Production Builds

For production builds, we will run tsc --noEmit in addition to Vite's build command.
Update package.json:

"scripts": {
  "build": "tsc --noEmit && vite build",
}
Enter fullscreen mode Exit fullscreen mode

The --noEmit flag prevents the compiler from generating JavaScript output files.


7. Convert Files to .jsx and .tsx

Rename files accordingly:

  • .js.jsx
  • .ts.tsx

Why is this required ?
Vite avoids processing all .js and .ts files for JSX or TypeScript syntax by default to prevent unnecessary overhead, which could slow down the development server and build times. Instead, by explicitly using .jsx and .tsx extensions, Vite selectively applies transformations only to the files that require them. When encountering these files, Vite leverages esbuild to efficiently transform JSX or TypeScript syntax into standard JavaScript, ensuring faster builds and a more optimized development workflow.


8. Add Absolute Path Support & Path Aliasing

In the Vite configuration file, paths can be resolved using the config.resolve property. Since manually defining numerous paths can be tedious, we use the vite-tsconfig-paths package. This package enables Vite to resolve imports using TypeScript’s path mapping automatically.

Install vite-tsconfig-paths:

npm install --save-dev vite-tsconfig-paths
Enter fullscreen mode Exit fullscreen mode

Update vite.config.ts:

import tsconfigPaths from "vite-tsconfig-paths";
import path from "path";

export default defineConfig({
  plugins: [tsconfigPaths()],
  resolve: {
    alias: {
      "@assets": path.resolve(__dirname, "src/assets"),
    },
  },
});
Enter fullscreen mode Exit fullscreen mode

If any of your path aliases do not work as expected with vite-tsconfig-paths, you can manually define them as shown above.


9. Add SVG Support

Install vite-plugin-svgr:

npm install --save-dev vite-plugin-svgr
Enter fullscreen mode Exit fullscreen mode

Update vite.config.ts:

import svgr from "vite-plugin-svgr";

export default defineConfig({
  plugins: [svgr()],
});
Enter fullscreen mode Exit fullscreen mode
Import SVG files as components:
import ChartIcon from "@assets/icons/Charts.svg?react";
Enter fullscreen mode Exit fullscreen mode

10. Configure Production Build & Target

Update vite.config.ts:

export default defineConfig({
  build: {
    outDir: "build",
    target: "es2021",
  },
});
Enter fullscreen mode Exit fullscreen mode
  • build.outDir specifies the output directory for the built files. By default, Vite outputs the build to the dist folder, but here it is customized to match CRA's default output directory.

  • build.target sets the JavaScript target version for the output files. The default value is "modules", ensuring compatibility with modern browsers that support ES module syntax.

  • For older browser compatibility, refer to the Vite Browser Compatibility Guide


11. Add Testing with Vitest

Install testing dependencies:

npm install --save-dev vitest jsdom @testing-library/react @testing-library/jest-dom @vitest/ui
Enter fullscreen mode Exit fullscreen mode

Update tsconfig.json:

"compilerOptions": {
    "types": ["vitest/globals"]
},
Enter fullscreen mode Exit fullscreen mode

Update package.json:

"scripts": {
  "test": "vitest --ui"
}
Enter fullscreen mode Exit fullscreen mode
  • The --ui flag in vitest --ui starts the Vitest UI, providing a visual test runner interface. It allows you to view test results, rerun specific tests, and debug failing tests interactively within a browser-based interface.

Update vite.config.ts:

export default defineConfig({
  test: {
    globals: true,
    environment: "jsdom",
    setupFiles: /* <path to test setup file> */,
    include: ["src/**/*.test.*"],
  },
});
Enter fullscreen mode Exit fullscreen mode

Update test setup file:

import { expect } from "vitest";
import matchers from "@testing-library/jest-dom/matchers";

// extends Vitest's expect method with methods from react-testing-library
expect.extend(matchers);
Enter fullscreen mode Exit fullscreen mode

Update Test Files:

  • describe, expect, it, and test work the same as in Jest
  • Replace jest.fn() with vi.fn()
  • For further changes, refer to Vitest Migration Guide

12. Adding Code Coverage

We will integrate Istanbul to generate code coverage reports. Other available option is v8

Installation:

npm install --save-dev @vitest/coverage-istanbul
Enter fullscreen mode Exit fullscreen mode

Update Script in package.json:

"scripts": {
  "test:coverage": "vitest run --coverage"
}
Enter fullscreen mode Exit fullscreen mode

Update vite.config.ts:

export default defineConfig({
  test: {
      coverage: {
        provider: "istanbul",
        reporter: ["lcov"],
        include: /* Specify directory or files here */,
      },
    },
});
Enter fullscreen mode Exit fullscreen mode

13. Enable Source Maps & Add Bundle Visualizer

To enable source maps in Vite, update the build.sourcemap option in the Vite configuration:

Update vite.config.ts:

export default defineConfig({
  build: {
    sourcemap: true,
  },
});
Enter fullscreen mode Exit fullscreen mode

Since Vite uses Rollup for production builds, we will install rollup-plugin-visualizer to visualize source maps.

Install rollup-plugin-visualizer:

npm install --save-dev rollup-plugin-visualizer
Enter fullscreen mode Exit fullscreen mode

Update vite.config.ts:

import { visualizer } from "rollup-plugin-visualizer";

export default defineConfig({
  plugins: [visualizer()],
});
Enter fullscreen mode Exit fullscreen mode

To prevent the visualizer from running with every build, we will create a separate script for it.
Update package.json:

"scripts": {
  "stats": "vite build && open stats.html"
}
Enter fullscreen mode Exit fullscreen mode

14. Setting Global Variable

Vite does not define a global field on window like Webpack does. Some libraries rely on this behavior because Webpack, being older, has established it as a common practice.

Update index.html:

<script>
  window.global = globalThis;
</script>
Enter fullscreen mode Exit fullscreen mode

15. Update storybook support

Install @storybook/react-vite

npm install --save-dev  @storybook/react-vite
Enter fullscreen mode Exit fullscreen mode

Update .storybook/main.ts:

const config = {
  framework: {
    name: "@storybook/react-vite",
    options: {},
  },
  stories: ["../src/**/*.mdx", "../src/**/*.stories.@(js|jsx|ts|tsx)"],
};

export default config;
Enter fullscreen mode Exit fullscreen mode

16. Update Sentry Configuration

Update vite.config.ts: Additionally, to enhance the debugging process, we will upload source maps to Sentry.

import { sentryVitePlugin } from "@sentry/vite-plugin";

export default defineConfig({
    plugins: [
      sentryVitePlugin({
        org: /* <Sentry Org Name> */,
        project: /* <Sentry Project Name> */,
        authToken: /* <Sentry Auth Token> */,
        sourcemaps: {
          filesToDeleteAfterUpload: ["./build/**/*.map"],
        },
        release: {
          name: /* <Your app release version or build no> */,
        },
      }),
    ],
});
Enter fullscreen mode Exit fullscreen mode
  • filesToDeleteAfterUpload option deletes the source map after the sourcemaps are uploaded to sentry

17. Remove CRA and Webpack-Related Configurations

Finally, delete unnecessary Webpack-related files and dependencies, including:

  • Remove react-scripts commands
  • Uninstall Jest-related packages
  • Uninstall the following Webpack-related dependencies:

    • webpack
    • webpack-bundle-analyzer
  • Sentry webpack package : @sentry/webpack-plugin

  • Storybook webpack packages : @storybook/builder-webpack5, @storybook/manager-webpack5

References

Contribute

If you feel any step can be improved, more details can be added, or new steps should be included, please feel free to contribute!

Check out the repository and submit a pull request:

GitHub Repository

Top comments (0)