I’ve been using Create React App for years now. I didn’t want to deal with the Webpack configuration. Usually I’m not a fan of tooling, I just want to code and get things done.

More often than not you need to apply some customization to Create React App, and that is not possible out of the box. You need to use tools like Craco or React App Rewired. This can be troublesome because they are not always in sync with the CRA releases, but usually, with a minimum effort, you can bend CRA just enough to suite your needs.

But in the last six months I hit a limitation of Create React App that I could not override without ejecting. CRA doesn’t allow imports outside of the src folder, and the dev server doesn’t update when a dependency in the node_modules folder changes. This is a problem when you work in a monorepo project. For instance, if you have a monorepo with this folders:

  |
  +- Packages
    +- Components
    +- App1
    +- App2

You want your dev server to update App1 and App2 whenever the components they import from the Components folder change.

I couldn’t find a way to achieve this with CRA, if you know how, please drop me a note in comments.

So I began experimenting alternative solutions to CRA. I played with Vite in a project using solid js and I decided to give it a try.

I discovered that bootstrapping a React project with vite is quite easy and, all in all doesn’t require much more work than with CRA.

This is my recipe. It is quite long, but the only actually required step is the first one. All the others are optional and depends on your desired setup. Almost all of them, except the 3 and 4, are required also with CRA.

1 Bootstrap a react project with vite

This is the only required step, that will bootstrap a fully functioning React application, just like Create React App does.

npm init vite (from the proposed options select react and typescript)

npm install

I prefer to change the dev script to start, for sake of habit and to be able to omit the run command invoking the script. Then you can run the dev server with:

npm start

Vite does not perform type checking, so to type check add this npm script to package.json:

  "type-check": "tsc --noEmit",

2 Install styled components

Using styled components you don’t need any CSS pre/post-processor, and no related tool or configuration.

Alternatively, CSS modules are supported out of the box by Vite.

npm i styled-components

npm i -D @types/styled-components

And delete any css file eventually present in the bootstrapped project.

3 Install and configure prettier and eslint

I’m tired of discussion on how to format code. In the projects I manage the team can adopt whatever format rules they like, as long as it can be automatically enforced by prettier.

Eslint is a super useful tool to capture possible errors at write time. Along with TypeScript is the foundation of the testing trophy.

npm i -D eslint

npm i -D --save-exact prettier

npm i -D eslint-plugin-react

npm i -D eslint-plugin-react-hooks

npm i -D @typescript-eslint/eslint-plugin@latest

Create or copy these files from a previous project:

  • .eslintignore
  • .eslintrc.json
  • .prettierignore
  • .prettierrc

To enable format on save on vs code run ctrl/cmd + shift + p “format document with…” and select “configure default formatter”, selecting prettier. On my machine this is necessary, even if Prettier is already configured as the default formatter.

4 Add Jest and React Testing Library

Jest is my go to choice for JavaScript testing, and React Testing Library is the best tool I know to test React applications.

npm i -D jest jest-environment-jsdom

npm i -D @testing-library/react @testing-library/jest-dom @testing-library/user-event

add

"test": "jest",
"test:watch": "jest --watchAll=true",

to scripts in package.json.

Unfortunately jest needs Babel to run: npm i -D @babel/preset-react @babel/preset-typescript @babel/preset-env

add the files

babel.config.cjs with this content:

module.exports = {
  presets: [
    [
      '@babel/preset-env',
      {
        targets: {
          node: 'current',
        },
      },
    ],
    '@babel/preset-react',
    '@babel/preset-typescript',
  ],
}

jest-setup.ts with this content:

import '@testing-library/jest-dom'

add the jest entry in the package.json

  "jest": {
    "testEnvironment": "jsdom",
    "setupFilesAfterEnv": [
      "<rootDir>/jest-setup.ts"
    ]
  }

5 .env

The .env files are supported out of the box by Vite, variables must be prefixed with VITE_and accessed by import.meta.env.VITE_XXX.

6 GIT

I don’t need to introduce Git, do I?

git init

6.1 Pre-commit hook

By default git hooks are stored in the .git/hooks folder, where they can not be versioned. To change the folder (e.g. to .githooks):

git config --local core.hooksPath .githooks/

Then you can install pretty quick, to run Prettier on staged files:

npm i -D pretty-quick

add

"pretty-quick": "pretty-quick --staged"

to the scripts in the package.json

create the file ./.githooks/pre-commit with this content:

#!/bin/sh

npm run pretty-quick && npm run lint

and make it executable:

chmod 755 .githooks/pre-commit

6.2 Pre-push hook

create the file ./.githooks/pre-push with this content:

#!/bin/sh

npm run test

and make it executable:

chmod 755 .githooks/pre-push

6.3 Commit-msg hook

Install Commitlint and the conventional commit conventions:

npm i -D @commitlint/cli @commitlint/config-conventional

add

"commitlint": "commitlint --edit $1",

to npm scripts in package.json

create the file ./.githooks/commit-msg with this content:

#!/bin/sh

npm run commitlint

and make it executable:

chmod 755 .githooks/commit-msg

create the file commitlint.config.cjs

with this content:

module.exports = {
  extends: ['@commitlint/config-conventional'],
}

7 Ladle

I struggled for years with the Storybook installation, configuration and maintenance. Countless times I found it broken after an update in the project dependencies. The last time I tried to install it in an existing project it broke the project’s build pipeline.

I decided then enoughs is enoughs and looked for an alternative. Ladle is a drop in replacement for Storybook, that uses the same story format and runs on Vite. Is super easy to install and run. Hope it will last the test of time.

npm i @ladle/react

add a story

run with pnpm ladle serve

You will notice that the installation added just one single dependency to your package.json file.

8. Redux toolkit

I love Redux, so I really love Redux Toolkit, that solves one of the few redux issue, verbosity, and I really really love RTK Query, that helps handling API calls. So:

npm install @reduxjs/toolkit

9. React i18 Next

Usually an application needs to be multilingual. React i18 next is the de facto standard to handle multilanguage in React:

npm i react-i18next i18next i18next-browser-languagedetector i18next-xhr-backend

add the src/i18n.ts file and import it in the main.tsx file

add translations in the public/locales/en/translation.json file

10. Enjoy

So here we are, the setup is ended. You now have a fast starting, fully fledged, React Application, with everything you need to develope a pro app.