The Best File Structure for Production-Grade React.js Applications

Ankur Boyed
Beavr Labs
Published in
4 min readJul 4, 2023

Building a production-grade frontend comes with the challenge of dealing with large amounts of code, including UIs, state management, and API integrations. With small projects, integrating these aspects into their own components is enough to get the job done. However, as the size of your project increases from ten files to hundreds, the importance of file structure design becomes an important aspect for maintaining a highly readable and modular codebase. In this article, I’ll discuss my experience building a codebase from the ground up for a client that wanted to build a Bitcoin exchange.

Why is it important to organize react apps in production environments

Production-grade React apps need to be understandable and extensible to prevent technical debt and to allow hires to hit the ground running as fast as possible. Imagine showing your hire a cluttered, disorganized workspace, versus a clean one where every resource is clearly organized and labeled. Doing any task in the messy environment would demoralize them as they spend hours of their time searching for the tools they needed to get the job done. Strong code design allows people to spend time working on what really matters — writing code.

Since React apps have so many interwoven aspects to them, such as shared context, state management and API integrations, a strong file structure empowers devs to align their code design decisions with previously written code, and encourages them to break down their thousand-line files into more understandable and modular chunks.

File structure can also decrease the potential for technical debt. When building the Bitcoin exchange for my client, I initially decided to use react-query hooks which were embedded directly inside the components that needed them. However, my team later decided to transition over to tRPC, which required us to rewrite dozens of lines of code in our ~200-file codebase. If we wrote our own custom hook for fetching and instead used that everywhere, we could have eliminated hours of work.

What is the optimal structure

The structure we’ve found to be the best fitted for our use case is a feature-based file structure with globally accessible code.

This structure works best for codebases with a lot of features. For the Bitcoin exchange we built, we needed to accommodate for functionalities like user authentication, onboarding, deposit and withdrawal of BTC/Canadian Dollars, BTC and CAD trading.

Under this feature-based structure, each feature lives in its own folder housing a set of frontend functionalities. Each feature may include hooks, context, layouts, components, and services. Let’s dive into what each one entails:

Context: Shared react context relevant to more than one component within a feature. This helps dodge excessive prop drilling. For instance, in the deposit modal, we utilize the context file to fetch the user’s account information and deliver it to the necessary components in the form of a react hook.

Hooks: Custom react hooks exclusive to the feature. We use this to furnish readily available functions to handle our deposit modal routers.

Components: React components intended for use within larger layouts. They’re essentially the building blocks of our UIs.

Layouts: Folders housing overarching UIs for the feature, stitched together with components, hooks, and services.

Services: This folder is a home for functions that abstract the API integrations we make, eliminating the need to call them directly inside your components.

But what if some functionality needs to be accessed across different features? No worries! Just replicate these folders in your global source folder, and place any shared hooks, context, layouts, components, and services in them.

One more global folder you should consider is ‘Lib’. This one’s dedicated solely for API integrations, allowing you to abstract the integration into functions accessible by the services that need them.

Let’s take a look at an example.

A file structure for large scale React apps

How to refactor your code to a feature-based structure

If you’re thinking of switching your existing file design to this one, don’t spend 2–4 weeks refactoring the entire codebase. Instead, for every new feature you build, carve out some time to gradually refactor 1–2 existing features to the new structure, factoring in the refactor time into the total time taken to complete the feature. This approach allows for progressive improvement while still getting new features out the door, and will likely get a thumbs-up from your boss or client keen on seeing fresh functionality added to their app.

--

--