Using react (& friends) with vite - New generation javascript tooling goes brrrr...⚡
callmeberzerker Sun Jul 25 2021
In this post I will try to summarize all the choices I made when building a starter (aka. template) for React using the new generation fast-tooling like vite (powered by esbuild).
Oh no a big wall of text -> you go and look at the github repo.
… But first some history 📚
In times long gone, most of us in the React world were happy using create-react-app
(hence abbreviated to CRA
) as our template project. Heck even apple
(yes that apple) was using CRA
to deploy a promotional site by using CRA
scaffolding. And we all accepted the limitations of the scaffolding that was provided by us, even though they were reasonable but still in some niche cases limiting.
The core tech-stack behind CRA
are webpack
as a bundler, babel
as a transpiler and jest
for testing. There is also TypeScript
(hence abbreviated TS
) typechecking being done by tsc
(the typescript compiler) - but without using TS
to bundle the application.
So what changed?
If you’ve ever used webpack
& babel
you might not need introduction; they are kinda slow. I will not delve into what makes them “slow” but they definitely need several seconds to bundle/transpile your code.
Frustration with the slowness of our javascript based tooling arose. A tool like esbuild
(made by Evan Wallace) showcased that tooling which is compiled to native (in Go
programming language) can speed the transpilation/bundling process 10-100 times.🤯
Then thanks to Evan You (creator of vue
framework) who figured out that serving the project files over native ES modules and using esbuild
(not babel
) to transpile the code (JSX
, TypeScript
, etc…) we can achieve almost instantaneous dev server
startup and have super smooth HMR
out of the box. Win.
The core tech-stack behind vite
are esbuild
for “transpilation” during development
, rollup
for production bundling (why not esbuild
is because esbuild
is still somewhat new and not with extensive plugin
ecosystem) and it’s own unique way of handling and serving files during development. That’s why vite
has many plugins prefixed vite-plugin-
that tries to bridge that disconnect between rollup
and esbuild
.
All of this is great and you can get a nice react
project setup going just by creating a vite
app, where you can choose react
-> and then react-ts
as your template. It even comes out of the box with react
’s own fast-refresh
that basically means on every file change (while the dev server
is running) your new code will be replaced via HMR
with your React component state being preserved. 🔥
Setting up the template
So I started out from vite
react-ts
template and went on to try to configure all the technologies I like to use…
Enter pain…
styled-components
So first of all since I like to use styled-components in my React projects I wanted to configure styled-components-macro which uses babel
(duh) but it was straight-forward to setup in vite due to it being abstracted in a vite-plugin-babel-macros.
Just as I was thinking I dodged a bullet having to configure babel
, I went on configuring jest
to run on my project…
Jest
First a foreword vite
probably will have first class jest
support but it’s not there yet (and I am kinda skeptical now how much of it can be safely abstracted away). You can watch this issue when it eventually lands. At the time of this writing there was still some work that needed to be done on jest
side to support vite
’s async transformers.
Some of the solutions offered in the comments there weren’t good for me since it kinda broke my VS Code Jest Runner
extension.
If you ever configured jest manually you know that thing is complex as all hell. It has bazillion of configuration knobs, has partial support for ES modules
. (ES Modules
were made stable
in Node
-land just recently)
Since vite
is doing its transforms w/o babel
you kinda have to use babel-jest
as your transformer
in jest.config.js
.
… which means - welcome back babel
I missed (configuring) you! 😅.

The last time I had to manually tweak babel
plugins
, presets
was 4-5 years ago (thanks CRA
!) so once more unto the breach. After I fixed it for the tests
(which was arduous process with many trials and errors, even though it’s couple lines of code) it broke dev
(???) and vice versa. If you are asking how did babel
, which was supposedly not running in dev
(or build
), broke the dev
build - it’s because since vite-plugin-babel-macros requires babel
under the hood and if it detects .babelrc
file in your project (which needs to be there for babel-jest
) it also runs all the presets and turns it into cjs
which is not supported by vite
. So bye bye declarative configuration formats welcome back babel.config.js
-> I used process.env['NODE_ENV']
to conditionally include presets/plugins based on it and declared victory. (to be honest I could also tried to configure babel-jest
inline in jest.config.js
but I was somewhat wading through errors at this point left and right… so this was Good™)
Other tech choices
I will just list a general libraries that most using React
are using -> prettier
, react-router-dom
, TS
.
GraphQL
I use custom script download-graphql-schema.js to download the schema.graphql
from my GraphQL api and then generate TS
types out of it.
husky & lint-staged
I use husky to run pre-commit
hook that in combination with lint-staged
will run those files through prettier
if applicable.
react-final-form
I use react-final-form as a go-to form library -> it’s battle-tested and I highly encourage you to try it out.
eslint
Configured with my hack-and-slash way of configuring eslint
-> if a rule I deem necessary I add it -> if something annoys me and I don’t see the benefit I remove it. I highly encourage you to do the same.
Also had to use eslint.js
(instead of a static format like .eslintrc
) because I want to read the schema.graphql
so I can configure graphql eslint plugin to validate my gql
tagged string literals for possible violations (ie. fetching non-existent fields).
react-testing-library
If you want to run component tests react-testing-library
is your best choice. You can take a look at a test sample here
urql
I really like the API (and the underlying architecture/thought process) of urql
graphql client. Built by the guys from Formidable
it really is the best and light(er) alternative to Apollo
. Highly recommended it.
react-router (v6)
Even though still in beta (v6 that is), I’ve been using it in a real app for the past 3 months and have found no issues. I think a stable release is coming soon - but it’s definitely an improvement over v5.
TypeScript
Nothing special here, just configuration of paths
so we don’t have none of that ../../../SomeComponent.tsx
nonsense but stable paths like src/shared/components/SomeComponent.tsx
. Be sure to also configure your vite/rollup configuration to resolving those paths (see here)
Closing thoughts
To configure some javascript libs to play nice to each other can be pain, each comes with certain expectations that don’t align with other libs expectations. Each library has it’s own configuration file so enjoy the config file explosion at the root of the folder 😅. The whole node
ecosystem is also going through a quirky phase with ESM
becoming officially supported and the general shift from cjs
-> so expect a few libraries to have “bad” package.json
entry point definitions for cjs
vs esm
. The general rule of thumb there is if you find an issue with such a package -> create an issue at their github repository.
This starter has all my preferred tech stack choices -> but you can easily remove/add parts that suit your need. You don’t like GraphQL
but like REST
-> rip off anything GraphQL
and feel free to add react query. You don’t like final form and prefer formik -> swap it out.
If you use this template or find a way to improve the setup (or make things more idiomatic) feel free to reach me via twitter (link below 👇).
Happy hacking everyone! 🍻