Fastest Frontend Tools in 2022
Tags: javascript, webdev, tutorial, beginners
Published: 2022-10-05 01:08:39

[View original post](https://cpojer.net/posts/fastest-frontend-tooling-in-2022).

I tried tons of frontend tools this year in my pursuit to optimize my [Developer Experience](https://cpojer.net/posts/principles-of-devx). I published an incredibly fast minimal template with sensible defaults which you can use to quickly spin up new projects: [`cpojer/vite-ts-react-tailwind-template`](https://github.com/cpojer/vite-ts-react-tailwind-template).

This is _not_ a does-it-all starter kit. The template comes with the essential tools for frontend development with minimal sensible defaults to make it easy to use, quick to get started, and adaptable to any frameworks on top. It is tuned for performance, not just in terms of actual speed, but also to maximize the time you stay focused while writing code to increase how quickly you can ship.

> “Perfection is achieved, not when there is nothing more to add, but when there is nothing left to take away.”

## Technologies

- [Vite](https://vitejs.dev/)
- [Tailwind](https://tailwindcss.com/)
- [pnpm](https://pnpm.io/) (_Optional_)
- [ESLint](https://eslint.org/) & [Prettier](https://prettier.io/)
- [TypeScript](https://www.typescriptlang.org)
- [React](https://reactjs.org/)

## Vite

[Vite](https://vitejs.dev/) is the best and fastest dev server I've used. I was skeptical at first, but now I don't enjoy working on any front-end project that isn't using Vite. With [esbuild](https://esbuild.github.io/) under the hood, it builds whole projects faster than it takes others to hot reload a single change. While other bundlers can be made fast, Vite does it all out of the box with almost no configuration required.

If you haven't used it, try it on a small project and don't look back.

## Tailwind

[Tailwind](https://tailwindcss.com/) is another project I was initially skeptical about. After a year of using it, Tailwind's tradeoffs make sense to me. The way it abstracts styling into a system of CSS variables instead of direct CSS property assignments is genius. I recommend using it if you want sensible defaults for styling, smart composition for styles, and consistency across your project. The way it handles dark-mode styles is nice, too.

**Alternatives considered:**

- [Emotion](https://emotion.sh/docs/introduction): Use `emotion` if the above criteria don't apply to you, like if you have a well-defined design system at your company, or you have highly specific styling needs.
- Many other CSS-in-JS Libraries. Emotion is currently my favorite one.

## pnpm

[`pnpm`](https://pnpm.io/) is the JavaScript package manager that won my heart. It's a joy to use for basically anything related to package management or monorepos, it is fast, and offers escape hatches when you are running into problems with third-party packages. However, while I updated many projects to use `pnpm` and recommend it to everyone, this template is not opinionated about the package manager you are using.

<Note>
  I've found that for long-running projects it's ideal to update your
  dependencies often. Depending on how many automated tests there are and at
  which stage of development a project is in, I might even update dependencies
  daily. This allows me to isolate issues in third-party updates to a smaller
  set of changes and ensures I never fall too far behind to make updates a
  chore.
</Note>

## ESLint & Prettier

[ESLint](https://eslint.org/) and [Prettier](https://prettier.io/) are widely adopted and you are likely using them already. They both serve overlapping concerns around code style, and on the surface, it seems like a good idea to run Prettier within ESLint. However, upon profiling why ESLint was slow on a bunch of projects, I noticed prettier consistently taking about 50% of the total ESLint runtime. If you are formatting your documents on save, there is no point in running Prettier within ESLint at the same time.

For CI or local test runs, I now run `prettier --cache --check .` and `eslint --cache .` as separate commands in parallel. Both projects now support a `--cache` flag to reduce the amount of work they do during local development. You should use these flags!

Despite the existence of Prettier, arguments about code style such as how to sort ES module imports still exist. Manually sorting ES modules wastes time, and usually leads to losing context when you are writing code and then have to navigate to the top of a file to modify your import statements. I love using the [`@trivago/prettier-plugin-sort-imports`](https://github.com/trivago/prettier-plugin-sort-imports) plugin which automatically sorts new imports, and works perfectly together with TypeScript's auto-import feature. Similarly, [`prettier-plugin-tailwindcss`](https://github.com/tailwindlabs/prettier-plugin-tailwindcss) automatically sorts Tailwind classes in your code.

Finally, my default [ESLint config](https://github.com/cpojer/vite-ts-react-tailwind-template/blob/main/.eslintrc.cjs) and [Prettier config](https://github.com/cpojer/vite-ts-react-tailwind-template/blob/main/prettier.config.cjs) are designed for _consistency_. I am not opinionated about which specific order some things like ES module imports are, but **I do care about having a consistent order within a single project**.

<Note>
  I'm incredibly excited about the potential of [Rome
  tools](https://rome.tools/). Rome's formatter and linter are close to
  production ready, and hopefully when I refresh this post next year, I'll be
  able to drop a bunch of paragraphs and replace them with just `rome`.
</Note>

## npm-run-all

I like running all tests and checks while developing locally. `npm-run-all` parallelizes your scripts and fails instantly if one check fails, making sure it doesn't slow you down. Despite the name it works works with all JavaScript package managers

```json
"scripts": {
  "build": "vite build",
  "dev": "vite dev",
  "format": "prettier --write .",
  "lint:format": "prettier --cache --check .",
  "lint": "eslint --cache .",
  "test": "npm-run-all --parallel tsc:check lint lint:format",
  "tsc:check": "tsc"
}
```

## ECMAScript Modules in Node.js

ES Modules in Node.js (_ESM_) are great in isolation, but the ecosystem integration and legacy third-party packages are making ESM hard to use. I spent way too much time trying to figure out how to use ESM with Node _without breaking everything with cryptic error messages_. Like seriously, I attempted it at least five times unsuccessfully and with trade-offs, I wasn't willing to make. Here are my requirements:

- Use native ESM.
- Fast JS compilation.
- Immediately restart scripts when files change.

**[I have since figured it out](https://twitter.com/cpojer/status/1553877399447764992).** Here is how to do it:

- Run `pnpm add -D ts-node @swc/core nodemon`
- Add `"type": "module"` to your `package.json`.
- In your `tsconfig.json`, add:

  ```json
  "ts-node": {
    "transpileOnly": true,
    "transpiler": "ts-node/transpilers/swc",
    "files": true,
    "compilerOptions": {
      "module": "esnext",
      "isolatedModules": false
    }
  }
  ```

- Create a `script.ts` file, run `chmod x script.ts` and execute it via `./script.ts`:

  ```ts
  #!/usr/bin/env node --no-warnings --experimental-specifier-resolution=node --loader ts-node/esm

  console.log('Your code goes here.');
  ```

- To instantly restart your scripts when a file changes:

  ```ts
  #!/usr/bin/env NODE_ENV=development node_modules/.bin/nodemon -q -I --exec node --no-warnings --experimental-specifier-resolution=node --loader ts-node/esm

  console.log('This processes instantly restarts when a file changes.');
  ```

It's a bit unfortunate that we have to use `swc` for scripts when we are already using `esbuild` for the frontend. However, any `esbuild` related solution was much slower in my testing.

**Alternatives considered:**

- Before switching to the above, I used to use [`ts-node-dev`](https://github.com/wclr/ts-node-dev) but it does not support ESM or `swc` properly.
- Direct use of `esbuild` or `swc` to bundle the script/app before running it. This solution took much longer to restart on file changes.
- Given my use of `vite`, I would like to use [`tsx`](https://github.com/esbuild-kit/tsx) in the future but after multiple attempts, it always added a 10x performance overhead for API requests in development and I ran out of time figuring out why.

## TypeScript

[TypeScript](https://www.typescriptlang.org/) (and to a lesser extent [React](https://reactjs.org/)) are unwritten standards at this point. TypeScript especially offers an incredible productivity boost to any project. I assume all new frontend projects use TypeScript unless there is a really strong reason not to, and I'm just including this paragraph for completeness and to show my appreciation that's been ongoing since January 11th 2013. Thanks TypeScript team ❤️

![Initial Commit with TypeScript from 2013](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/5i29vlqx0ywc1y3sfitm.png)

## VS Code Extensions

Here are four extensions that keep me in the flow state for longer, especially [All Autocomplete](https://marketplace.visualstudio.com/items?itemName=Atishay-Jain.All-Autocomplete) and [Error Lens](https://marketplace.visualstudio.com/items?itemName=usernamehw.errorlens):

```bash
code --install-extension bradlc.vscode-tailwindcss
code --install-extension dbaeumer.vscode-eslint
code --install-extension esbenp.prettier-vscode
code --install-extension usernamehw.errorlens
```

If you want more VS Code protips, I wrote about my [favorite VS Code extensions](https://cpojer.net/posts/the-perfect-development-environment#favorite-extensions) just recently.

---

Frontend development has never been more fun. I still get excited whenever someone creates a game-changing tool. Many of the above tools have impressed me with how fast and delightful they are. I dare you to show me even faster and better tools 😜