Migrating your React app from Webpack to Vite
Tags: javascript, react, vite, tutorial
Published: 2022-04-07 22:47:13

---
title: Migrating your React app from Webpack to Vite
published: true
description: The missing guide on how to migrate your React app from Webpack to Vite.
tags: javascript, react, vite, tutorial
//cover_image: https://direct_url_to_image.jpg
---

## What is Vite?

[Vite](https://vitejs.dev/) is a "next generation front-end tooling", as its maintainers claim. Instant dev server, fast Hot Module Reload, easy configuration, optimized production builds - it's all there. 

But… Vite's documentation doesn't say how to migrate your existing Webpack app 🤷 Worry not! In this guide, we'll get through this together!

## Change your repo to ES modules

In your `package.json`, add the following entry:

```json
  "type": "module",
```

Embrace modernity! Reject tradition! That's why we're switching to Vite anyway!

## Install Vite and its plugins

```sh
npm i --save-dev vite @vitejs/plugin-react vite-plugin-simple-html
```

or

```sh
yarn add vite @vitejs/plugin-react vite-plugin-simple-html --dev
```

## Replace scripts

In your `package.json`, you'll probably have scripts similar to these:

```json
  "build": "NODE_ENV=production webpack",
  "dev": "NODE_ENV=development webpack serve",
```

`build` command, invoked by `npm run build` or `yarn build`, builds your app for production. `dev` command starts a development server.

These scripts needs to be replaced with:

```json
  "build": "vite build",
  "dev": "vite serve",
```

On top of that, you can add one extra command:

```json
  "preview": "vite preview"
```

`preview` command will start a server running your app built for production.

Let's run the development server!

```
  vite v2.9.1 dev server running at:

  > Local: http://localhost:3000/
  > Network: use --host to expose

  ready in 261ms.
```

👁👄👁 Wow, that is fast. 

## Teach Vite where the root is

If you started the development server now, Vite will look for `index.html` in your project's root directory. If it's anywhere else, Vite will not be able to find it and will display an empty page instead.

To fix this, you need to either move `index.html` to your root directory, or specify a different root directory for Vite to look for `index.html` in. In my case, it's located at `src/index.html`.

You can do this by adding root directory path to your commands, like this:

```json
  "build": "vite build src",
  "dev": "vite serve src",
```

However, you can also do this by creating a `vite.config.js` file in your project root. You will need it in a short while anyway, so why not create one now to keep all the configuration in one place?

```js
import { defineConfig } from 'vite';

export default defineConfig({
  root: 'src',
  build: {
    // Relative to the root
    outDir: '../dist',
  },
});
```

## Configure `vite-plugin-simple-html`

Now that Vite knows where to find your `index.html` file, it will try and parse it.

You may encounter an error like me:

![](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/x8m125n9gtkyx4q631ex.png)

How to deal with it? I was using `HtmlWebpackPlugin`'s `templateParameters` option to dynamically inject custom title and other info into `index.html` file, like so:

```html
  <title><%= title %></title>
```

```js
new HtmlWebpackPlugin({
  template: 'index.html',
  templateParameters: {
    title: env === 'production' ? 'My site' : `My site [${env.toUpperCase()}]`,
  },
}),
```

Thankfully, we can do the same with `vite-plugin-simple-html`. In your `vite.config.js`, add the following bits:

```js
import simpleHtmlPlugin from 'vite-plugin-simple-html';

export default defineConfig({
  // …
  plugins: [
    simpleHtmlPlugin({
      inject: {
        data: {
          title: env === 'production' ? 'My site' : `My site [${env.toUpperCase()}]`,
        },
      },
    }),
  ],
});
```

## Add entry module to your `index.html` file

At this point, your `index.html` file should be served just fine. But the app still won't load!

If you used Webpack, you probably have also used `html-webpack-plugin` to handle injecting `<script>` tag(s) with your entry module(s) to `index.html`.

Vite will not inject these tags automatically. You will need to add them by yourself. For example:

```html
<script type="module" src="./index.jsx"></script>
```

Aaah, that's better. Something came to life.

![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/ugbjxree5xrnxymx6gza.png)

### Configure `@vitejs/plugin-react`

Okay, as you can see, we're not *quite* there yet. We need to configure `@vitejs/plugin-react` to make it work with React.  

If you still used classic JSX runtime, your app may already load at this point, but you'll want to follow these steps anyway. This plugin will not only handle automatic JSX runtime (the one thanks to which you don't need to manually import React in every file), but also add features like Fast Refresh, enable Babel integration, and much, much more.

Add it to your `vite.config.js` file like so:

```js
import react from '@vitejs/plugin-react';

export default defineConfig({
  // …
  plugins: [
    // …
    react({
      // Use React plugin in all *.jsx and *.tsx files
      include: '**/*.{jsx,tsx}',
    }),
  ],
});
```

## Aliases

At this point, if you were using aliases (to write e.g. `import Button from 'src/components/Button'` instead of `import Button from '../../../../../components/Button'`), your build may fail. Easy fix!

Webpack:
```js
  resolve: {
    alias: {
      src: path.resolve(__dirname, 'src'),
    },
  },
```

Vite:
```js
  resolve: {
    alias: {
      src: path.resolve(__dirname, 'src'),
    }
  },
```

Wait a second, same thing? 🤔 Yes! Well, kinda. You have [more options to choose from](https://vitejs.dev/config/shared-options.html#resolve-alias), but most of the time, your aliases should work when copied over.


## Static files handling

By default, files from `public` directory placed *in your root directory* are going to be copied over at build time. If  you have these files elsewhere, you can use `publicDir` option like so:

```js
export default defineConfig({
  // …
  publicDir: '../public',
});
```

## SVG handling

If you used to import SVG icons to use not as static files but rather as React components, @cassidoo just wrote [an interesting article on that matter]( https://dev.to/cassidoo/importing-svg-files-as-react-components-with-vite-l3n) - be sure to check it out!

## Babel plugins

You might not need Babel at all, as `@babel/preset-env` and `@babel/preset-react` are of no use.

But sometimes Babel plugins may still come in handy. For example, there's a [plugin to remove PropTypes](https://www.npmjs.com/package/babel-plugin-transform-react-remove-prop-types) you can use to make bundle size a bit smaller, and there's a [dedicated plugin for styled-components](https://www.npmjs.com/package/babel-plugin-styled-components) that makes development and testing easier by, among others, adding component display names.

`@vitejs/plugin-react` will come to the rescue here, with `babel` option. For example, to add `babel-plugin-styled-components` plugin:

```js
    react({
      // …
      babel: {
        plugins: ['babel-plugin-styled-components'],
      },
    },
```

## The `process.env.*` problem

I was using `process.env.NODE_ENV` in a bunch of places in my app. This resulted in the following error being thrown in the console:

```
Uncaught ReferenceError: process is not defined
```

In Vite, you can use `import.meta.env.*` instead. For example, `process.env.NODE_ENV` can be replaced with `import.meta.env.NODE_ENV`.

## Enjoy!

Now you should see your app, powered by Vite!

We're not done yet; we'll still need to tweak a few things before running it in production. For this, you'll have to wait for the second part of this guide. Subscribe to get notified!

## Cleaning up

You can safely remove these dependencies, which are now unused:

- `core-js` (unless you've been using it directly)
- `webpack` (duh)
- `webpack-cli`
- `webpack-dev-server`
- `*-loader` (e.g. `babel-loader`, `style-loader`)
- `*-webpack-plugin` (e.g. `html-webpack-plugin`, `mini-css-extract-plugin`
- `@babel/preset-env`
- `@babel/preset-react`
- `@pmmmwh/react-refresh-webpack-plugin`
- `react-refresh`

`webpack.config.js` Webpack config file can also be deleted.

`babel.config.js`, `babel.config.json`, or `.babelrc` can be deleted, provided that you didn't use it as your Babel config in `@vitejs/plugin-react` configuration.

## Anything missing?

Do you think there's anything else that needs to be addressed, that may be a common problem when migrating from Webpack to Vite? Please, please let me know in the comments!