Starting a Frontend Project in 2022

How would I start a new frontend project in 2022? By frontend project, I really mean a single page web application that talks to an API of some sort via HTTP (whether that API is RESTful, GraphQL, JSON-RPC, or something else entirely).

First of all, I would try not to. That's the subject of a different article, however. Let's assume that we're already past the point of deciding whether or not we should.

In fact, let's make some assumptions about what we're building:

If you don't share these assumptions, then I would just say use whatever you are most familiar and comfortable with for rapidly building something and then throw it away when you're done.

Tooling

Use Webpack, Babel, and PostCSS. Learn how to write your own Webpack configuration and stick close to the defaults. ESLint everything. Pick an autoformatter like Prettier and use it everywhere.

If you can spare the reduced functionality and know Go and JavaScript well, consider using esbuild.

Set up a development proxy for your bundler (e.g. webpack-dev-server) and make it easy to point it at any instance of your API so your frontend developers don't have to run the full stack. This will make it easier to reproduce production bugs (just point your development setup to the production API and load an example).

TypeScript is amazing, but use it with the understanding that it may go the way of CommonJS modules as type annotations slowly become a mainstream ECMAScript feature in future versions of the standard. TypeScript may be a superset of today's JavaScript, but it may be incompatible with tomorrow's JavaScript. A long lived project may be better off avoiding it.

Similarly, CSS-in-JS and SASS or LESS like superset languages come and go. Most CSS-in-JS solutions are also sadly underspecified. Focus instead on mastering CSS Grid and Flexbox based layouts and CSS variables before investing into tooling. Consider sticking to plain CSS using a convention like Block Element Modifier (BEM) instead of getting caught up with trying to automatically scope your CSS. If you must use something, SASS is well-specified and has many quality implementations.

Use Storybook.

Process

Stick to low fidelity mockups for your early stage work. Avoid tools that encourage high fidelity mockups that will eat up everyone's time.

Instead, invest in your continuous integration (CI) setup. It should be easy to push working examples of new features for anyone to review. It should be so easy that you can deploy a few alternative examples during a meeting and tear down the losers afterwards.

You need to build a design system.

As you mock up your user interface (UI), look for common user experience (UX) patterns and design reusable components around them. These are the building blocks of your design system.

Focus on building these components out as Storybook components. If you're using React, use contexts, CSS variables, and naming conventions to provide theming support. Most importantly, only implement the functionality you need -- don't spend time generalizing.

You want to build features, not frameworks. Sometimes you're also better off deprecating and replacing components in your design system as it matures. Early generalization of your design system is a waste of time.

Architecture

Avoid frameworks built on top of frameworks. These are for quickly building one-off projects, and you want to build something that will last at least a decade.

Avoid convenience libraries, state management libraries, and dependencies that just adapt pure JavaScript libraries to your framework of choice. Pick a good framework like React or Vue and use it in its purest form.

Use plain old JavaScript for your state management. Any interaction with the outside world or the backend to fetch, create, update, or delete data should be through a plain JavaScript data access layer of your own design. Make this layer easy to mock.

Any asynchronous functions in your data access layer should support cancellation. Currently that looks like using AbortSignal and passing an instance into the function. If your function signatures have a consistent design, then you can always create a convenience layer at some later point for your component framework (i.e. using RxJS for Angular or custom hooks for React).

If you do use a state management library, focus on using it consistently throughout your business logic layer. Compose this with your UI components and try to keep them focused on presentation rather than behavior.

Security

Does your application have a need to be embedded inside of other web pages and browsing contexts? If not, don't allow it.

Design your application with authentication in mind. It should be immediately obvious when your session has expired and possible to reauthenticate without losing your existing page state.

Watch out for any mechanism that could be used to control redirects or inject content via query parameters and path elements.

Don't try to sanitize user input using regular expressions. If you can't render into a sandboxed iframe with no origin, then either fully escape user input or parse it and safely generate an escaped version from the parsed structure.

Regularly vet your dependencies and maintain your own NPM registry mirror.

Finishing

As you build out your initial application, there may be multiple pivots. Buying into too many layers of tooling or dependencies can quickly cripple your ability to swap out ideas and rewrite portions of your code. Instead of working on your application, you'll be working on updating dependencies.

Keeping it simple let's you focus on your problem. The first few months will take longer, but as you grow a reusable design system, you will speed up without the usual caveats that come with using someone else's.

Great projects never end. These are the prescriptions I would give myself if I were trying to start a new frontend project in 2022, but they're just for the beginning. Regularly take the time to reflect on your decisions and update your choices accordingly.

Finally, don't be afraid to ignore every suggestion here to quickly stand up rapid prototypes that you throw away. This is where convenience libraries, webpack project wrappers, and frameworks built on other frameworks can be extremely useful, because once you've proven out an idea, you can throw away all the cruft with the original prototype. The easier the code is to delete, the more adventurous you should be.