# CRA migration Vite(v4.1.1)
## 1. Remove Create React App
- uninstall packages
```bash
npm uninstall react-scripts
```
- remove package.json entries:
**package.json**
```json
"scripts": {
"start": "react-scripts start",
"build": "react-scripts build",
},
```
## 2. Install Vite
```bash
npm install -D vite @vitejs/plugin-react
```
### Update `tsconfig` (path aliasing included)
**tsconfig.json**
```json
{
"compilerOptions": {
"target": "ESNext",
"useDefineForClassFields": true,
"lib": ["DOM", "DOM.Iterable", "ESNext"],
"allowJs": true,
"skipLibCheck": true,
"esModuleInterop": true,
"allowSyntheticDefaultImports": true,
"strict": true,
"forceConsistentCasingInFileNames": true,
"module": "ESNext",
"moduleResolution": "Node",
"resolveJsonModule": true,
"isolatedModules": true,
"noEmit": true,
"jsx": "react-jsx",
"paths": {
"~/*": ["./src/*"]
},
"baseUrl": "."
},
"include": ["./src"]
}
```
### Update `package.json`
```json
"scripts": {
"dev": "vite",
"dev:clean": "npm run dev -- --force",
"build": "tsc && vite build",
"serve": "vite preview --port 3000",
}
```
### Create config file
Add **vite.config.ts** at the root of your project
```typescript
import path from "path";
import { defineConfig } from "vite";
import react from "@vitejs/plugin-react";
import { createHtmlPlugin } from 'vite-plugin-html'
const ENV_PREFIX = 'REACT_APP_'
// https://vitejs.dev/config/
export default defineConfig(({ mode }) => {
const env = loadEnv(mode, 'env', ENV_PREFIX)
return {
root: './',
plugins: [
react({
babel: {
plugins: [
[
'babel-plugin-styled-components',
{
displayName: true,
fileName: false,
},
],
],
},
}),
createHtmlPlugin({
minify: true,
/**
* Data that needs to be injected into the index.html ejs template
*/
inject: {
data: {
title: 'index',
injectScript: '<script src="./inject.js"></script>',
},
tags: [
{
injectTo: 'body-prepend',
tag: 'div',
attrs: {
id: 'tag',
},
},
],
},
}),
envCompatible({ prefix: ENV_PREFIX }),
],
resolve: {
alias: [{ find: '@', replacement: path.resolve(__dirname, 'src') }],
},
server: {
port: 3000,
open: env.SERVER_OPEN_BROWSER === 'true',
},
base: '/',
build: {
outDir: 'build',
assetsDir: 'assets',
},
}
})
```
## 3. Environment variables
Vite handles environment variables in a different way than CRA, [Read here](https://vitejs.dev/guide/env-and-mode.html),
to avoid complete refactoring and preserve backwards-compatibilty we can use a custom [plugin](https://github.com/IndexXuan/vite-plugin-env-compatible).
```bash
npm install -D vite-plugin-env-compatible
```
**vite.config.ts**
```typescript
import envCompatible from "vite-plugin-env-compatible";
const ENV_PREFIX = "REACT_APP_";
// https://vitejs.dev/config/
export default defineConfig(({ mode }) => {
return {
plugins: [envCompatible({ prefix: ENV_PREFIX })],
};
});
```
## 4. Static analysis
By default, type checking & linting in CRA was built into dev server, we can implement this in Vite with a [plugin](https://github.com/fi3ework/vite-plugin-checker).
I will omit running `eslint` here since it can pollute terminal & browser console output with many warnings, it's also pretty slow in larger codebases.
Additional linting can be handled by IDE setup, git hooks & PR checks (e.g. github actions).
```bash
npm install -D vite-plugin-checker
```
**vite.config.ts**
```typescript
import checker from "vite-plugin-checker";
// https://vitejs.dev/config/
export default defineConfig(({ mode }) => {
return {
plugins: [
checker({
overlay: false,
typescript: true,
}),
],
};
});
```
## 5. Babel plugins
We were adding custom babel plugins to CRA webpack setup via [craco](https://github.com/gsoft-inc/craco).
This is how we can handle it in Vite (example includes usage of [Styled Components plugin](https://styled-components.com/docs/tooling#babel-plugin)):
```typescript
// https://vitejs.dev/config/
export default defineConfig(({ mode }) => {
return {
plugins: [
react({
babel: {
plugins: [
[
"babel-plugin-styled-components",
{
displayName: true,
fileName: false,
},
],
],
},
}),
],
};
});
```
## 6. SVG Components
CRA allows to transform `.svg` files into React components.
```tsx
import { ReactComponent as Logo } from "./logo.svg";
```
In order to make this work with Vite we will need a [plugin](https://github.com/pd4d10/vite-plugin-svgr)
```bash
npm install -D vite-plugin-svgr
```
```typescript
import svgrPlugin from "vite-plugin-svgr";
// https://vitejs.dev/config/
export default defineConfig(({ mode }) => {
return {
plugins: [
svgrPlugin({
svgrOptions: {
icon: true,
// ...svgr options (https://react-svgr.com/docs/options/)
},
}),
],
};
});
```
You also might need to add additional declarations file to satisfy TypeScript.
Create `src/custom.d.ts`:
```typescript
declare module "*.svg" {
import React = require("react");
export const ReactComponent: React.FC<React.SVGProps<SVGSVGElement>>;
const src: string;
export default src;
}
```
## 7. Index.html
- Move `index.html` from `/public` to the root of the project
- Remove any `%PUBLIC_URL%` references from the file
```html
<!-- Before -->
<link rel="icon" href="%PUBLIC_URL%/favicon.ico" />
<!-- After -->
<link rel="icon" href="./favicon.ico" />
```
- Add Entry script tag to 'index.html'
```html
<!-- Add to body -->
<script type="module" src="./src/index.tsx"></script>
```
- Embedding JavaScript:
CRA supported [ejs](https://ejs.co/) out of the box, we will need additional [plugin for Vite](https://github.com/anncwb/vite-plugin-html):
```bash
npm install -D vite-plugin-html
```
**vite.config.ts**
```typescript
import html from "vite-plugin-html";
// https://vitejs.dev/config/
export default defineConfig(({ mode }) => {
return {
plugins: [
createHtmlPlugin({
minify: true,
/**
* Data that needs to be injected into the index.html ejs template
*/
inject: {
data: {
title: 'index',
injectScript: '<script src="./inject.js"></script>',
},
tags: [
{
injectTo: 'body-prepend',
tag: 'div',
attrs: {
id: 'tag',
},
},
],
},
}),
],
};
});
```
## 8. Additional features
### Open tab when server starts
Add possibility to configure opening a new tab when dev server is launched (it was turned on by default in CRA):
**.env**
```bash
SERVER_OPEN_BROWSER=true
```
**vite.config.ts**
```typescript
import { defineConfig, loadEnv } from "vite";
const ENV_PREFIX = ["REACT_APP_", "SERVER"];
// https://vitejs.dev/config/
export default defineConfig(({ mode }) => {
const env = loadEnv(mode, "env", ENV_PREFIX);
return {
server: {
port: 3000,
open: env.SERVER_OPEN_BROWSER === "true",
},
};
});
```