How to Setup a Large Scale React application

How to Setup a Large Scale React application

How to organize files and folders? How to enforce a style guide? What type system, linter and tester to use? How to maintain code quality? This post is based on the series of posts: Modernizing a jQuery frontend with React.

This post is based on the series of posts: Modernizing a jQuery frontend with React. If you want to get a better overview of the motivation for this post we recommend you first read our initial post.

Nowadays, it is very easy to set up a small React application, or to start one from scratch. Especially if you use create-react-app. Most projects probably just need a few dependencies (for example, for state management and for internationalization) and a src folder with at least a components folder. I guess this is how most React projects start. Typically, though, as the project grows the number of dependencies, components, reducers and other shared utilities tends to increase in a, sometimes, not so controlled manner. What do you do when it is no longer clear why some dependencies are needed or how they work together? Or when you have so many components it becomes difficult to find the one you need? What do you do when you want to find a component but you don’t really remember its name?

These are just some examples of the questions we came across while rebuilding our frontend at Karify. We knew that the number of dependencies and components was eventually going to grow out of hand. That meant we needed a plan that would be scalable enough to keep up with future development. This plan involved defining conventions for our file and folder structure, code quality and also defining the overall architecture. Most importantly, all this should be easy to pick up by new developers without requiring them to have much insight into all our dependencies and code style.

At the moment of writing, we have about 1200 JavaScript files of which 350 are components with 80% of unit test coverage. Since we still believe in the architecture and conventions we created we thought it would be a good idea to share them. In the following sections, I’ll go through how we set up our project and also some of the lessons we learned.

How to organize files and folders?

We went through multiple phases until we figured out how we wanted to organize our React frontend. Initially, we thought of placing it in the same repository as our jQuery frontend. However, the imposed folder structure of our backend framework made this option undesirable. Next, we thought of moving it into a separate repository. This worked well at first, but with time we started thinking about creating other frontends, such as a React Native frontend, which motivated the need for a component library. This led us to split this new repository into two separate repositories: one for the component library and one for the new React frontend. Even though this seemed like a good idea, it resulted in a very complex review process. The relationship between changes in the two repositories became unclear. Finally, we chose to bring them together again into one repository, but this time as a monorepo.

We chose a monorepo because we wanted to create a separation between our component library and our frontend applications. The difference between our monorepo and others out there is that we don’t really need to publish the packages inside of it. For us, the packages serve only as a means for modularity and separation of concerns. It is especially useful to have different packages for every different application since we can specify different dependencies and scripts for each one of them.

We set up our monorepo using yarn workspaces with the following configuration in our root package.json:

"workspaces": [
    "app/*",
    "lib/*",
    "tool/*"
]

Now some of you might wonder why we didn’t simply use a packages folder like in other monorepos. Mainly because we wanted to create a separation between our applications and our component library. Besides that, we also knew we needed to create some tooling of our own. So, we came up with the folders you see above and here is an explanation for each one of them:

  • app: all packages in this folder refer to frontend applications like our Karify frontend, some internal frontends and also our Storybook;
  • lib: all packages in this folder expose shared utilities to our frontend applications and are as application-agnostic as possible. These packages basically form our component library. Some examples would be our typographymedia and primitive packages;
  • tool: all packages in this folder are Node.js specific and either expose tools that we created ourselves or are configuration and utilities for tools we depend on. Some examples would be webpack utilities, linter configurations, and a file system linter.

Regardless of where we place them, all our packages always have a src folder and optionally have a bin folder. The src folder of our app and lib packages might contain some of the following folders:

  • actions: contains action creator functions of which the return value can be passed to the dispatch function from redux or useReducer;
  • components: contains component folders with their respective definition, translations, unit tests, snapshots and stories (if applicable);
  • constants: holds constant values reused across different environments and also different utilities;
  • fetch: holds type definitions for the payloads from our API and the respective async actions to retrieve them;
  • helpers: holds utilities that do not fit in any of the other categories;
  • reducers: contains reducers to be used in our redux store or useReducer;
  • routes: holds route definitions to be used in react-router components or history functions;
  • selectors: contains helper functions that read or transform data from our redux state or our API payloads.

This folder structure allows us to write really modular code since it creates a clear separation of concerns between different concepts defined by our dependencies. This helps with the lookup of variables, functions, or components in the repository, independently of us knowing if they exist or not. Furthermore, it also helps keeping the contents of these folders to a minimum, which in turn makes them easier to process.

One challenge with this new folder structure is to make sure we stick to it. It is tempting to start creating different folders in different packages and organizing files differently between them. While that might not always be a bad idea we would end up with a mess if we don’t do it consistently. To help with that, we created a file system linter which I’ll describe in more detail in the next section.

programming typescript react tdd javascript

Bootstrap 5 Complete Course with Examples

Bootstrap 5 Tutorial - Bootstrap 5 Crash Course for Beginners

Nest.JS Tutorial for Beginners

Hello Vue 3: A First Look at Vue 3 and the Composition API

Building a simple Applications with Vue 3

Deno Crash Course: Explore Deno and Create a full REST API with Deno

How to Build a Real-time Chat App with Deno and WebSockets

Convert HTML to Markdown Online

HTML entity encoder decoder Online

How to Build Meme Generator with React, React Hooks and TypeScript

The best way to learn something is by doing. It works even better if it means working on something for fun. So, how about learning about React, React hooks and TypeScript by building your own meme generator? This tutorial will show you how to do it.

Top VSCode Extensions for React, React Native, JavaScript and Productivity

Bunch of VSCode Extensions that improve quality of your coding time no matter what stack you are using. In this post, you'll see Top VSCode Extensions for React, React Native, JavaScript and Productivity

Build React Components for Codebases that use JavaScript & TypeScript

Learn how to build React components for codebases that use JavaScript and TypeScript. Learn how we can use TypeScript with React and built components that are usable within both TypeScript and JavaScript files.

Working with IndexedDB in TypeScript — React

In this article, we are going to take a look at how we can work with IndexedDB in TypeScript while working with a React application. Let’s take a look at what is IndexedDB. IndexedDB is a low-level API for client-side storage of significant amounts of structured data, including files/blobs. This API uses indexes to enable high-performance searches of this data. While Web Storage is useful for storing smaller amounts of data, it is less useful for storing larger amounts of structured data.

How to use Functional Programming in your React app | React Next

In the past few years, the buzz around functional programming has been growing, but it can be challenging to apply theoretical concepts to everyday work. How can we make the code we work on more functional? What advanced patterns can you use, and why should you do that? And what do React hooks have to do with all this? In this talk we’ll have a look at real-life examples and patterns you can use to make React apps more functional.