Why and How to Migrate Your React App from CRA to Vite
Tags: react, vite, webdev, refactorit
Published: 2024-06-10 07:19:59

At the time(10/06/2024) of writing this article, CRA has been effectively in a semi-dead state for the past two years. It has not received any commits since last year nor has it received any important commits for the past two years in [CRA commit history](https://github.com/facebook/create-react-app/commits/main/). Issues have been pilling and none of them are being addressed in [CRA issues](https://github.com/facebook/create-react-app/issues).

![screenshot of CRA commit history showcasing that it has not received any commits for the past two years](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/91pxzyuqsmekl0u6gmvn.png)

So why is it in a semi-dead state and not dead, because the React team has not deprecated it, so, if it's not deprecated then it might have reached a level of maturity and might not need any new changes, so it is not in a semi-dead state but has reached a certain level of maturity.

Here is the tricky situation and that's why CRA is in a semi-dead state, it has not been deprecated but isn't receiving any updates not even security updates, along with that the new [React.dev](https://react.dev/learn/start-a-new-react-project) documentation doesn't mention CRA but suggests using React meta-frameworks like [Next](https://nextjs.org/) and [Remix](https://nextjs.org/) for new projects. You can read more about React's reasoning for it in this [github issue discussion](https://github.com/reactjs/react.dev/pull/5487#issuecomment-1409720741).

So React suggests using meta-frameworks for new projects, which means it should be fine to use CRA in the existing projects. you may ask. The issue with it is that it lacks many of the modern build tools features that we need in production applications and it might not be possible or you might not want to migrate to a meta-framework, which means either you have to eject out of CRA and maintain your webpack configuration or you can use another brilliant frontend build tool call Vite.

## Vite
[Vite](https://vitejs.dev/) is a modern frontend build tool created by [Evan You](https://x.com/youyuxi) (creator of [Vue.js](https://vuejs.org/)). Vite is framework agnostic and works on a plugin based approach.

For this article, we will specifically focus on migrating a React App from CRA to Vite.

## Why Vite?
- Vite's plugin based approach allows us to configure the project that best suits our use case by exposing low level APIs as much as possible without us needing to configure the underlying bundler.
- Vite is not a bundler but a frontend tool that intelligently uses [ESBuild](https://esbuild.github.io/) and [Rollup](https://rollupjs.org/) for their best use cases.
- Vite servers Native ESM code in the dev server mode for better start/reload times and HMR (Hot Module Replacement).
- Using ESBuilt, Vite creates a highly optimized prod bundle with support for better code splitting and tree shaking.
- Using plugins for configuring the porject means that we can pick and choose what we want to use for certain things.
- We can effectively create our own meta-framework using Vite, as it even supports things like SSR and SSG.

There are many more benefits of using Vite as discussed by the Vite team here [Why Vite?](https://vitejs.dev/guide/why.html)

Now, let's see how to migrate our existing CRA project to Vite.

> NOTE:
>  I will be providing overview implementations and pseudo code and commands in this article since my aim is not to give you a template but to guide you toward migrating your app that fits your needs. I will provide the necessary and important references throughout the article for your help.

## Getting Started

### Install Vite
We can start by installing Vite in our current project.
```bash
npm install vite --save-dev
```

### Install React Plugin
As Vite works on a plugin based approach, we will have to install one of the two official React plugins. You can read more about them and choose the one that suits your needs from here [@vitejs/plugin-react](https://github.com/vitejs/vite-plugin-react/tree/main/packages/plugin-react) and [@vitejs/plugin-react-swc](https://github.com/vitejs/vite-plugin-react-swc)
```bash
npm install @vitejs/plugin-react --save-dev
```

### index.html
During development, Vite is a server and it uses index.html as the entry point. Therefore we need to make a few changes to the index.html file.
- Move index.html file to the root of the project.
- URLs inside index.html are automatically rebased so there's no need for special %PUBLIC_URL% placeholders and they can be removed.
- Add below script tag that allows it to reference the Javascript source code. (preferably at the end of the body tag, as that's were a fresh Vite project puts it)
```js
<script type="module" src="/src/index.tsx"> </script>
```
## Configuration
### Config file
Next, we will have to create a `vite.config.js` or `vite.config.ts` file at the root of the project based on whether if its a Javascript or a Typescript project.
```js
import { defineConfig } from 'vite'
import react from '@vitejs/plugin-react'

export default defineConfig({
  base: '/',
  plugins: [react()]
})
```
You can read more about all the configuration options at [Config](https://vitejs.dev/config/)

### Typescript
To resolve imports using TypeScript's path mapping you will have to use one of the community plugins for Vite. You can read more about it here [vite-tsconfig-paths](https://github.com/aleclarson/vite-tsconfig-paths)

### Environment Variables
Vite exposes env variables on the special `import.meta.env` object, which are statically replaced at build time. To prevent accidentally leaking env variables to the client, only variables prefixed with `VITE_` are exposed.

Therefore, we will have to replace all the `process.env.` with `import.meta.env.` and replace the `REACT_APP_` prefix with `VITE_`.
```env
# CRA env variable
REACT_APP_MY_ENV = 'some value'
process.env.REACT_APP_MY_ENV

# Vite env variable
VITE_MY_ENV = 'some value'
import.meta.env.VITE_MY_ENV 
```

### SVGs
To use SVGs and SVGs as a React component in our Vite project, we will have to use another awesome community plugin called `svgr()`.

You can check out [vite-plugin-svgr](https://github.com/pd4d10/vite-plugin-svgr) to learn more about how to install and use `svgr()` and all the configuration options that it exposes.

### JSX in JS
By default, Vite will not resolve any JSX file with `.js` extension, even though there are workarounds to resolve this, it would be better to migrate all the JSX files with `.js` or `.ts` extension to `.jsx` or `.tsx` as this is the official recommendation from the Vite team.

To learn more about the reasoning behind this decision you can read this [Twitter/X](https://x.com/youyuxi/status/1362050255009816577) thread by Evan You.

### Browserslist
For a production build, you are likely using [Browserslist](https://github.com/browserslist/browserslist), by default Vite targets browsers that support [native ES Modules](https://caniuse.com/es6-module), [native ESM dynamic import](https://caniuse.com/es6-module-dynamic-import), and [import.meta](https://caniuse.com/mdn-javascript_operators_import_meta).

In Vite, legacy browsers can be supported via the official [@vitejs/plugin-legacy](https://github.com/vitejs/vite/tree/main/packages/plugin-legacy) plugin, it also provides Browselist like configuration.

### Build File
For the production build, Vite creates a `dist` directory while CRA creates a `build` directory, if you have a CI/CD pipeline you might have to make changes in it to look for `dist` directory or you can change the output directory name in the build options. You can read more about all the available [build options](https://vitejs.dev/config/build-options.html).  
   
## Update Scripts
Now, as most of our configuration part is done, we can move on to updating scripts in our `package.json` file.
### Dev Server
First, we will have to update our start script or dev server script.
Change your `start` script.
From:
```json
 "start": "react-scripts start",
```
To:

```json
 "start": "vite",
```
Now, we can also remove the `eject` command as it is no longer applicable.
Remove: 
```json
"eject": "react-scripts eject" <-- remove this
```

### Build
Before updating build scripts we will look into one more thing, for a production application you might be using something like [env-cmd](https://github.com/toddbluhm/env-cmd#readme) for managing multiple env files for multiple environments such as development, staging, and production.
Vite has a first class support for something called `modes`, we can utilize this to remove dependency on `env-cmd` for managing multiple environments. You can read more about it in [Env Variables and Modes](https://vitejs.dev/guide/env-and-mode.html#modes).

Now, let's update our build scripts.
From:
```json
"start": "vite",
"build:dev": "env-cmd -f .env.dev react-scripts build",
"build:staging": "env-cmd -f .env.staging react-scripts build",
"build:prod": "env-cmd -f .env.prod react-scripts build",
```
To:
```json
"start": "vite",
"build:dev": "vite build --mode dev",
"build:staging": "vite build --mode staging",
"build:prod": "vite build --mode prod",
```

#### Typescript
For Typescript we will have to prefix build scripts with `tsc && `.

### Unit Test
As you may have noticed, unit tests in CRA also depend on `react-scripts`
```json
"test": "react-scripts test"
```
There are two main ways in which you keep your existing unit tests, one is using `jest` directly as `react-scripts` uses it under the hood and another one is using `vitest` (this might require some migration but shouldn't be much work). 

I will not go into detail about migrating your unit tests, as every project has its own test setup, and generalizing it would be very difficult. You can read and learn more about both of them in [JEST docs](https://jestjs.io/docs/tutorial-react) and [Vitest docs](https://vitest.dev/).

## Testing
Now, as all configurations and scripts have been updated, we will have to do thorough testing of all the commands, features, and pages to make sure everything is working fine and fix any issues that might arise.

Once all the testing is done and we have verified that everything is working fine, we can finally remove the CRA dependency from our project.
```bash
npm uninstall react-scripts
```
Also, remove env-cmd or similar utility that we might not be using anymore.
```bash
npm uninstall env-cmd
```

## Extra
### Absolute Imports
Absolute import allows us to import components, utilities, and files using their absolute path with respect to the root of the project rather than a relative path from the current file, this makes imports more readable, easy to refactor, and overall improvement in DX as you don't have to wonder from where the file is being imported.

You can read more about how to implement absolute imports by googling it or you can read another great dev article [How To Create Absolute Imports In Vite React App: A step-by-step Guide](How To Create Absolute Imports In Vite React App: A step-by-step Guide) by [Andrew Ezeani](https://dev.to/andrewezeani).

### OpenSauced Interview with Evan You on Vite
If you are interested in how, when, and why Vite was created, how it became a dominant frontend building tool, and other interesting bits from Evan You. You can watch this [Why is Vite Everywhere? | Evan You](https://youtu.be/4_uYqae42uc?si=wgMuXkbhSsiHZhGn) interview by [OpenSauced](https://opensauced.pizza/).


## Like, Follow and Discuss
If you liked this article you can leave a reaction and if you disliked something you can leave your feedback in the comments.

If you think I missed any important point or want to discuss something more in detail, we can discuss it in the comments, as it will help others who will read it in the future and maybe that person could be our future self when we need to migrate another React App from CRA to Vite.

I like to connect and talk with people about tech in general and the events surrounding it. You can connect with me on [Twitter/X](https://x.com/idrisGadiX) and [LinkedIn](https://www.linkedin.com/in/idris-gadi/). You can also follow me on [dev.to](https://dev.to/idrisdev).