Back in 2018, I wrote a post about my web development set up and how I planned to go from a local dev environment to a cloud one. In the end it didn't happen. Two years have since passed, and I've revisited my views on it. Here's how I'd go about building a new web app today.

Design tool: Figma

I might or might not have a designer on the team. If there isn't and I have to design it myself, my design tool of choice is Figma. Many designers still use other tools like Sketch, Adobe Illustrator and some old school people even use Adobe Photoshop. Yes, I've met such people as recently as last year. But to me, they don't even come close to Figma. I might write in another article why I don't like Sketch nor Adobe Illustrator for UI/UX design. But here's why I like Figma so much:

  • Web-based tool. No installation required, I know I'm running the latest version automatically, and it doesn't matter which OS I use.
  • Components! Making components and instances of components is a life saver. You can change your base components, and your whole UI will update accordingly.
  • Auto-layout is amazing. Aligning things perfectly is a breeze, and everything resizes automatically.
  • Supports real-time collaboration and comments. This is great when collaborating with someone else.
  • Supports making click-through demos. Also great to show your customer and gather feedback.

Repository: GitHub

GitHub lets you create an unlimited number of private repositories for free. What's more, their pull request UI is great, and now you can use GitHub Actions to run automated tests on your pull requests. More on that below.

IDE: Visual Studio Code and soon Codespaces

Visual Studio Code is awesome, full of great plugins and what's more it's free. What more to ask? How about making it web-based so that I could code remotely? I would not have to download the repository onto my computer, set up my local dev environment nor install VSC. Wait, it already exists? Oh right, it's called Codespaces. https://github.com/features/codespaces. Well it's still a closed beta, and I haven't received the invite yet. But once it's out, I'll start using that! How things have changed since 2018.

I'll use the following plugins: ESLint, Prettier. I might write a separate page on how to set up these.

Enabling the ESLint plugin

Web framework: React, Hooks in TypeScript

React with Hooks + TypeScript. I like hooks because they are much more concise than classes, and I like the way they bring related logic together with hooks (see Hooks intro). Also I like TypeScript (see TypeScript in 5 minutes) because it is typed. It helps prevent so many bugs.

No Redux. I've found that for smaller webapps, the added complexity of notions and extra layers is not worth the benefits. Especially when using Firestore, since Firestore provides real-time updates.

To create the project, I use https://create-react-app.dev/. It sets up everything for you. The boilerplate code, example tests, the build scripts... No need to mess with Webpack configs. Also it has an option to use the TypeScript template. That's what I use.

Code quality: ESLint, Prettier

ESLint

By default, the project created by https://create-react-app.dev/ doesn't enforce many ESLint rules. I like having those rules around. It ensures consistent practices when working with a team. I'd add the following:

  • recommended ESLint rules
  • TypeScript rules
  • React rules
  • React Hook rules
  • Jest rules

See my full ESLint config below.

See https://eslint.org/docs/user-guide/configuring, https://github.com/typescript-eslint/typescript-eslint/blob/master/docs/getting-started/linting/README.md.

Prettier

Prettier enforces style. I like that too. With this, you never have to ask a teammate to fix their spacing again. Since Prettier conflicts with some ESLint rules, they have set up special ESLint config to disable the rules in question. For example "prettier/@typescript-eslint" will disable the rules of "plugin:@typescript-eslint/recommended", that would conflict with Prettier rules.

See https://prettier.io/docs/en/install.html and https://github.com/prettier/eslint-config-prettier/.

Final ESLint config

Btw, since the introduction of React's new JSX Transform, you don't need to always import React, so I disable two of the ESLint react rules, as documented here: https://reactjs.org/blog/2020/09/22/introducing-the-new-jsx-transform.html.

"eslintConfig": {
  "extends": [
    "eslint:recommended",
    "plugin:jest/recommended",
    "plugin:jest/style",
    "plugin:react/recommended",
    "plugin:react-hooks/recommended",
    "plugin:@typescript-eslint/recommended",
    "react-app",
    "react-app/jest",
    "prettier",
    "prettier/@typescript-eslint",
    "prettier/react"
  ],
  "rules": {
    "react/jsx-uses-react": "off",
    "react/react-in-jsx-scope": "off"
  }
}

UI Components: Ant Design or Material UI

Both give a large collection of easy to use components, and are well documented. Ant Design provides more than Material UI, like components to upload files.

Tests

Unit tests with Jest. Jest is nice. It will re-run tests as you modify your code. It will also compute code coverage. Also they support "snapshots", which is kind of a lightweight way of testing like screenshots. See https://jestjs.io/docs/en/snapshot-testing. For larger projects I might also want to write integration tests with React Testing Library, and Cypress for end to end tests. https://reactjs.org/docs/testing.html#tools

Services

Authentication: Firebase Auth

Firebase Auth handles sign up and sign in. I can pick which services to use such as authentication by phone number, Google, email and password... and they even have a feature for users to sign in anonymously.

Email: Mailgun

There are a few other services out there. I haven't tested them but Mailgun was awesome. Verifying domains, using the API, etc. was seamless, and the quotas are high enough that it's extremely cheap to use.

Database: Firestore

I love Firestore because it provides a great API that supports real-time updates. Let's say your todo app saves a new todo when the user clicks a button. As soon as the todo is saved, your TodoList component in your app that listens to todos in the database is notified and gets rendered again. No need for Redux's centralized store.

Server: None or Firestore functions

For simple apps, I don't even need a server. But if I have server-side tasks to do, Firestore functions might do the trick. If I really need heavy logic, I'll use Node.js, but I'll try to stay away from it if possible to reduce maintenance. Cause then I'd have to host it on a VM.

Hosting: Firebase Hosting

They have a simple enough command line to set up environments, then upload your builds to a given environment. Pricing is good too. https://firebase.googleblog.com/2016/07/deploy-to-multiple-environments-with.html

Continuous integration: GitHub Actions

GitHub actions are awesome, and you only need to read https://docs.github.com/en/free-pro-team@latest/actions/quickstart and https://docs.github.com/en/free-pro-team@latest/actions/learn-github-actions/introduction-to-github-actions to start writing your own workflow.

GitHub actions will run workflows on certain triggers. Mostly I want to run unit tests, ESLint and Prettier checks whenever a pull request is updated, and code is pushed to the main branch. Here are my workflows verbatim.

Unit tests

name: unit-tests
on: [push]
jobs:
  run-unit-tests:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v2
      - uses: actions/setup-node@v2
        with:
          node-version: "12"
      - run: npm install
      - run: npm test

ESLint

name: lint
on: [push]
jobs:
  run-linter:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v2
      - uses: actions/setup-node@v2
        with:
          node-version: "12"
      - run: npm install
      - run: npx eslint src --ext .js,.jsx,.ts,.tsx

Prettier

name: prettier
on: [push]
jobs:
  run-prettier:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v2
      - uses: actions/setup-node@v2
        with:
          node-version: "12"
      - run: npm install
      - run: npx prettier --check src