HarderEasier, Better, Faster, Stronger π΅
Introduction
Create React App (or CRA for short) is a must-have in the JavaScript ecosystem, used to "scaffold" / initialize a new React project. Most component-oriented frameworks have their own ("official") CLI tool for this kind of task: @angular/cli
a.k.a ng
, @vue/cli
a.k.a vue
, etc...
However, in recent years (2020), a new tool has emerged, popularized by the open-source community (and Evan You, its creator): Vite!
Like CRA which relies mainly on Webpack to provide a ready-to-use JS/TS project; Vite relies on ESBuild (and Rollup's intuitive interface) to create a new Web project. In 2023, Vite stands out as a major player in the current and future Web, by providing the following functionalities:
ESM Native Support
The power of ESM (ECMA Script Module) during development
CRA relies on CommonJS syntax, while adding Babel plugins to support modern functionalities, such as import
/export
, etc... On the other hand, Vite natively (and implicitly) supports .esm
syntax.
In other words, you can develop directly with async
/ await
(at the top level), or import your .tsx
/ .jsx
files directly into the application's entry point (index.html
) without worrying about the compilation phase.
ESMs (or explicitly .mjs
files) have their own context that allows them to evaluate the latest syntax supported by the JS environment (browsers / NodeJS). * Vite will then compile all this for you!
* NB: By setting the
type
key tomodule
in thepackage.json
, you convert your project to ESM format. The impact of this concerns your "classic".js
files, that's to say in CommonJS format. Typically configuration files, such as.eslintrc.js
orprettier.config.js
(which contains this statement:module.exports = {}
), will need to be suffixed.cjs
to work in an ESM environment. Indeed, a "module" project doesn't implicitly support the CommonJS syntax, as well as for the default projects (non-ESM), which will have to use the.esm
mention to natively support some advanced features.
Faster by default
- Time saving during development π
The switch from Webpack to Vite comes with a change at the development server level.
With Webpack, providing a development environment requires browsing all the resources (JS files, JSX components, and other modules...) to then launch the development server.
With native support for ECMAScript modules (made possible by ESBuild), Vite runs the development server first, then loads each resource as needed ("code-splitting" style).
- Time saving during compilation π
Another notable change with Vite (and React) is support for Speedy Web Compiler (SWC for short).
When switching from Webpack to ESBuild, you'll have the option of keeping your "good old" Babel compiler to package/build your application, or switching to its Rust equivalent (20x faster in theory).
Currently, not all Vite-based libraries benefit from this feature... But as far as React is concerned, there's a working plugin, so you might as well take advantage of it! π
Maintenance
In 2023, Vite is more than mature with these 4 major releases! Unlike Create React App, whose updates are increasingly slow, the open-source community wants to upgrade its new Frontend development tool! Moreover, Vite currently offers a choice of 7 component-oriented frameworks (with an out-of-the-box TypeScript environment), in addition to VanillaJS:
npm create vite@latest
β Project name: β¦ hello-world
? Select a framework: βΊ - Use arrow-keys. Return to submit.
Vanilla
Vue
β― React
Preact
Lit
Svelte
Solid
Qwik
Others
Some of these main frameworks * have already chosen to migrate their CLI tool to Vite (especially vue
, but also svelte
, which has "dropped" degit
in favor of this last one). Maybe it's time to get started!
* NB: Since version 16 of Angular (May 2023), the framework developed by Google offers an ESBuild "preset" to optimize development performance.
Migration (Step-by-Step)
Initialization
The 1st step of this migration work consists in initializing a new React environment based on Vite (rather than installing the dependencies needed for the project one by one...). To do that, I recommend that you follow instructions given by the following command: npm create vite@latest
You should end up with a similar structure, relatively close to Create React App:
/
βββ public/
βββ src/
β βββ assets/
β βββ App.css
β βββ App.tsx
β βββ index.css
β βββ main.tsx
β βββ vite-env.d.ts
βββ .eslintrc.cjs # Oh ! CommonJS Syntax :)
βββ index.html
βββ package.json
βββ tsconfig.json
βββ tsconfig.node.json
βββ vite.config.ts
If you look closer, especially in the package.json
, there's no longer any reference to react-script
(essential for CRA projects), which was responsible for executing Webpack's specific tasks (start
, build
, test
). This time, everything is based on Vite. *
* NB: In the same way, the
npm run preview
script available with Vite, is equivalent to thenpx serve -s build
recommended by Create React App (and Vercel) for testing the release of your Web application.
Configuration
NB: At this point, I invite you to set up a file formatting strategy (via Prettier ππ) and configure ESLint according to your needs.
As you can see, the 2nd step consists in configuring your new project. To do that, most of the work is focus on the vite.config.js
file.
If you went through the following CLI statement: npm create vite@latest
during initialization, you should already have an up-to-date configuration!
However, letβs add support for SVGs (from import
statement) and your potential module reference aliases: npm i -D vite-plugin-svgr
The baseUrl
property associated with paths
is essential for TypeScript compilation, as well as the use of the "hyperlink" feature from the IDE. You can also use the configuration below in a jsconfig.json
file for JavaScript (non-TS) projects.
Another file to modify (perhaps the most important!? π€), the entry point of your application: the index.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<!-- Before -->
<!-- <link rel="icon" type="image/png" href="%PUBLIC_URL%/favicon.png" /> -->
<!-- After -->
<link rel="icon" type="image/svg+xml" href="/vite.svg" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Vite + React + TS</title>
</head>
<body>
<div id="root"></div>
<!-- Oh ! ESM Syntax :D -->
<script type="module" src="/src/main.tsx"></script>
</body>
</html>
βοΈ Copy / π Paste
Ctrl +C / +V
The next step is simply to copy / paste the sources present in your previous project / to your new Vite structure. If you have not left the CRA context, simply move the content of the src
folder to that of Vite, while paying attention to references in the JavaScript entry point of your project: main.tsx
.
NB: Think about aliases! Make sure that all of your imports match your
vite.config.js
/tsconfig.json
orjsconfig.json
configuration.
Also consider changing access to environment variables from process.env
to import.meta.env
(like ESM syntax).
Tests !!! βοΈ
Vitest is the new Jest
Let's not forget the tests during this migration work!
CRA recommends using Jest for unit testing (which is already a good choice). If you have a pre-configured project with Jest, this step should go rather "fast" π
Let's start by installing the new dependencies related to Vitest (replacing Jest): npm i -D vitest @vitest/coverage-v8 @testing-library/jest-dom @testing-library/react jsdom
Then let's configure your project to support Vitest as a global variable:
Finally, replace all references to Jest with Vitest, as follows:
import { vi } from 'vitest';
import { fireEvent, render, screen } from '@testing-library/react';
import MyComponent from '../MyComponent';
test('should triggers click event', () => {
const onClickMock = vi.fn(); // jest.fn();
render(<MyComponent onClick={onClickMock} />);
fireEvent.click(screen.getByText('Click Me'));
expect(onClickMock).toHaveBeenCalled();
});
Vitest's API is very close to Jest, since it's entirely inspired by this last one. With a few exceptions: importActual. It's up to you to run some tests: npx vitest
; or with code coverage: npx vitest --run --coverage
NB: It's possible to emulate Vitest in a smoother interface with the
@vitest/ui
dependency. I invite you to test that, by simply executing the following instruction:npx vitest -- --ui
At the end of this last step, you will have fully migrated your Web application from CRA to Vite (or at least 99% of your project). Well done! π And... Welcome to the future ππ
Conclusion
The migration from Create React App to Vite doesn't change the content of the application... However, it significantly improves the Development eXperience (DX), especially with the "Hot Module Replacement" (HMR) support, allowing you to change project resources, without having to restart the development server.
On the other hand, the React community can take advantage of the power of Rust, thanks to a ready-to-use plugin, to gain performance when compiling the project. I guess other component-oriented frameworks will follow that way...
Finally, the Test Runner change (from Jest to Vitest) also boosts performance when running unit tests, due to a lighter (and modern) library.
Despite its young age (compared to others), Vite is increasingly used (as well as ESBuild) with more than 6 million NPM downloads in August 2023 (i.e. NPM Trends), unlike Webpack, which sees its number of users decline...
NB: Note that, Vite benefits from a multitude of community plugins, including support for Electron, Storybook and PWA mode.
Choosing Vite means having a head start in Web development, because of its simplicity, its compatibility with Rollup, but also due to the ability to change frameworks (at any time) without major breaking changes: "one bundler to rule them all!"
Top comments (3)
Nice post! I recently started using Vite because it allows you to build in Library Mode, so you can publish react components to npm. I think Vite has still got a way to go to be as easy to use as CRA, but it seems way more versatile and performant. πͺ
With Next.js being widely used nowadays, would you recommend migrating CRA to Vite or React project to Next.js? As Next.js has other advantages of SSR, SSG, backend coding etc.
I think, it's an another fight... I see NextJS as a "meta-framework" of React, so at an another level. NextJS has many advantages, especially page routing, Incremental Static Regeneration, etc... But, under the hood, it includes Webpack and not ESBuild as for Vite. On the other hand, NextJS uses the SWC compiler, which makes it fast and efficient (even with Webpack π). NextJS is great for websites since it makes SEO management easy (that's why I use it for my portfolio); but for a "business" webapp, simply start to develop with React (and Vite) is a solid foundation π