This post was originally published on my blog.
It's been quite a while since I wrote an article about how I structure my Node.js REST APIs. The article covered the approach of designing a well organized and maintainable folder structure for Node.js applications.
So today I don't want to talk about Node.js APIs, but about the architecture of React applications and answer the same question from the previous article a second time:
What should the folder structure look like?
And again: there’s no perfect or 100% correct answer to this question, but there are tons of other articles discussing this one on the internet too. This folder structure is also partly based on multiple of them.
One important thing to mention is that React does not really tell you how to organize your project, except the fact that you should avoid too much nesting and overthinking. To be exact they say: (Source)
React doesn’t have opinions on how you put files into folders. That said there are a few common approaches popular in the ecosystem you may want to consider.
Take a look at the linked source where you can read more about those common approaches. They won't be further discussed in this article.
The following structure and architecture is one that has proven maintainable and reliable for me. It might give you a help for designing your own project. Keep in mind that the following architecture is based on a application bootstrapped with create-react-app and written in JavaScript.
Directory: root
react-project
├── node_modules
├── public
├── src
├── package.json
└── package-lock.json
This structure is nothing special and shouldn’t be new to you. It’s actually a basic create-react-app setup. The interesting part here is the content of the src
folder which this article is about.
So what do we have in here?
react-project
├── api
├── components
├── i18n
├── modules
├── pages
├── stores
├── tests
├── utils
├── index.js
├── main.js
└── style.css
As you can see the application is primarily split into eight directories. From here on, we'll go top-down through the directories and examine each one.
Let’s start with the api
directory.
Directory: src/api
react-project
├── api
│ ├── services
│ │ ├── Job.js
│ │ ├── User.js
│ ├── auth.js
│ └── axios.js
The api
directory contains all services that take care of the communication between the React application (frontend) and an API (backend). A single service provides multiple functions to retrieve data from or post data to an external service using the HTTP protocol.
auth.js
provides functions for authentication and axios.js
contains an axios instance including interceptors for the outgoing HTTP requests and incoming responses. Moreover, the process of refreshing JWTs is handled in here.
Directory: src/components
react-project
├── components
│ ├── Job
│ │ ├── Description.js
│ │ └── Preview.js
│ └── User
│ │ ├── Card.js
│ │ ├── Create.js
│ │ └── List.js
If you're already familiar with React you should know that it's mainly component based. The components are actually the heart of every React application. The whole application, at least the presentational view, is built of many small components.
So what is a component? Source
Components let you split the UI into independent, reusable pieces, and think about each piece in isolation.
Imagine you have a website like Twitter or Facebook. The large website is made of many smaller pieces (components) that can be Buttons, Inputs or Widgets for example. Those pieces are put together to build ever more complex and larger ones. Each component has its own lifecyle and state management, whereby you can share a component's state with other ones.
Components are reused multiple times within the application to save the developer from writing redundant code.
Splitting the codebase into multiple components is not just a "React thing". It's a common pattern in software engineering to simplify the development process and the maintenance later on.
In React, a component is mostly a simple JavaScript function or a class. Usually, I create a new file for each single component. In some rare cases I group multiple of them (functions or classes) into a single file. Imagine a UserList.js
component which renders multiple elements of UserListItem
:
const UserList = ({ users }) => (
<ul>
{users.map(user => (
<UserListItem key={user.userId} user={user} />
))}
</ul>
)
const UserListItem = ({ user }) => <li>{user.name}</li>
Here, it makes sense to combine both into one file. Further, UserListItem
is probably not even used by any other component than UserList
.
Beside the components themselves, you can also add their stylesheets or tests to this directory.
Directory: src/i18n
react-project
├── i18n
│ ├── de.json
│ └── en.json
i18n
stands for internationalization and takes care of the language support of the application. The including JSON files are basically objects containg fixed constants as keys and their associated translations as values.
Therefore, the keys should be equal for each language file. Only the values (translations) differ from each other. You can easily query those language files later on by writing your own custom hook or component.
Directory: src/modules
react-project
├── modules
│ ├── logger.js
│ └── session.js
This directory includes some global modules that might be used for logging or as wrapper for the browser's LocalStorage
for example.
Directory: src/pages
react-project
├── pages
│ ├── Home
│ │ ├── components
│ │ │ ├── Dashboard.js
│ │ │ └── Welcome.js
│ │ └── index.js
│ ├── Login.js
│ └── Profile.js
The pages
directory includes the react-router-dom
paths accessed while navigating through the application. Here, we collect multiple components into a single larger one to display a complete page view.
A page might contain its own component
directory which includes "local" components that are only used on this page. For complex pages with a deep component tree you might want to check out the React Context API which makes it much easier to pass props along the tree and to handle a global "page state".
Directory: src/stores
react-project
├── stores
│ ├── language.js
│ └── user.js
This directory includes all global React states that can be accessed from any component within the application. While Redux is probably the most popular solution for managing global state I prefer to use zustand. It's very easy to get started with and its API is really straightforward.
Directory: src/tests
react-project
├── tests
│ ├── language.test.js
│ └── utils.test.js
The tests
directory includes tests that do not belong to certain components. This could be tests for the implementation of algorithms for example. Moreover, I validate and compare the keys of the language files I mentioned above to make sure I did not miss any translation for a given language.
Directory: src/utils
react-project
├── utils
│ ├── hooks
│ │ ├── useChat.js
│ │ ├── useOutsideAlerter.js
│ │ ├── useToast.js
│ ├── providers
│ │ ├── HomeContextProvider.js
│ │ ├── ToastContextProvider.js
│ ├── colors.js
│ ├── constants.js
│ ├── index.js
Here, we have a bunch of utilities like: custom hooks, context providers, constants and helper functions. Feel free to add more stuff here.
All together
Last but not least a complete overview of the project structure:
react-project
├── api
│ ├── services
│ │ ├── Job.js
│ │ ├── User.js
│ ├── auth.js
│ └── axios.js
├── components
│ ├── Job
│ │ ├── Description.js
│ │ └── Preview.js
│ └── User
│ │ ├── Card.js
│ │ ├── Create.js
│ │ └── List.js
├── i18n
│ ├── de.json
│ └── en.json
├── modules
│ ├── logger.js
│ └── session.js
├── pages
│ ├── Home
│ │ ├── components
│ │ │ ├── Dashboard.js
│ │ │ └── Welcome.js
│ │ └── index.js
│ ├── Login.js
│ └── Profile.js
├── stores
│ ├── language.js
│ └── user.js
├── tests
│ ├── language.test.js
│ └── utils.test.js
├── utils
│ ├── hooks
│ │ ├── useChat.js
│ │ ├── useOutsideAlerter.js
│ │ ├── useToast.js
│ ├── providers
│ │ ├── HomeContextProvider.js
│ │ ├── ToastContextProvider.js
│ ├── colors.js
│ ├── constants.js
│ ├── index.js
├── index.js
├── main.js
└── style.css
That’s it! I hope this is a little help for people who don't know how to structure their React application or didn’t know how to start. Feel free to give any suggestions.
Top comments (40)
Hi, thanks for sharing. This might sound opinionated but my personal preference is to stick to Next.js way of doing it, unless there's a specific and significant reason to deviate. Firstly - looking for the "best" way to structure a project is not a quest where an absolute truth can be reached - nor is it likely inventing anything beyond what's already out there - we've been at it for a while already, and all the principles for good code organisation have been formulated decades ago and apply cross-language. Additionally consider that Angular has an endorsed convention that's important for Angular-specific reasons, Vue.js has Nuxt.js that pretty much nails it, same as Next.js for React - you get a lot for free from just sticking to what they do. And if I was to inherit one of your projects - figuring out your private and undocumented organisation system would add noticeable overhead when first mentally indexing the codebase. If it was just common Next.js - I already know what stuff goes where and would onboard quicker, and depending on project size, complexity and time in development - my time savings would grow in significance. When working in a team - everyone might have slightly different tastes - having in inherent convention from outside the team that's well known and documented negates the need to seek compromises internally. I strongly suggest you should adopt Next.js straight away - I wager you'd come to appreciate it within 2 days of switching - speaking from personal experience here.
Hi, thanks for your feedback first of all :)
one of the most frequent feedback or "criticism" I get for this article is that I should rather stick to Next.js. I've never used Next.js or even took a closer look on it before but I have no doubts that it has a better folder structure / code organisation, higher scalability, cleaner conventions or whatsoever. Nevertheless, that's actually no real reason for me to abandon the idea of building an own React project structure.
I guess that's just my personal opinion but I'm not really a fan of statements like "you should rather go with framework X than framwork Y". If I was a beginner, which this article is written for, one of the last things I'd like to hear when learning a new programming language / framework is something similar to this.
Moreover, I think you can learn quite a lot by implementing your own folder structure, even if it might not be the best one or nothing like an "industry standard".
Absolutely, that's why I mention it at the beginning of the article :)
I might be wrong (because I never used Next.js) but for me it's hard to believe that there's such a huge difference regarding the architecture between a Next.js and "vanilla" React application that ends up in a significant time saving while getting familiar with the code. Depending on the project size the most time consuming part is the process of learning the project domains in my opinion.
Anyway, thanks for you suggestions. I'll definitely check out Next.js in the future.
Standards are best to follow when working on a team project. Where there are no standards, conventions should be followed. Here, if you are using Next.js, then you should use their conventions. Agreed.
This is a good enough structure that I follow in a similar way for my personal or small scale projects. For webapps that are going to potentially be touched by several developers I find quite useful to have feature folders to apply some kind of bounded context to those components and logic. Having a shared folder for example for a base api client or some utility functions that aren't feature dependent would be the point of re-use.
I tend to work in an environment where most components are complex compositions of atomic components coming, for example, from a component library.
In any case nice reading and if you just keep your frontends small and orchestrate them with SSR or more recently with module federation it can boost scalling capabilities.
Amazing!
Thanks, glad to help!
Thanks for sharing!
Could you explain the difference between
stores
andproviders
and the reason whyproviders
is inutils
folder andstores
inroot
separately?stores
are global states across the whole application managed by zustand, which is a state-management library.providers
are actually React Context providers. I use them mostly to make the state management for "large" pages with a deep component tree easier. I guess you could also put them into the directory of the according "page" component.So
stores
are accessible from anywhere within the application whileproviders
are mostly used for some specific pages.Thank you! Your explanation was very helpful to understand!
Interesting structure! I also use something similar to this, but it always gets a little questionable once a project grows. What does your structure evolve into when you have component A, and component A has custom styles, hooks, utils and tests?
In this case I would put all the files that belong to the component into one directory. So something like this:
Looks well structured.
Personally, I wouldn't put all tests in a separate folder, as I'd have to recreate to whole src structure inside of it, unless I'd stick with a tons of test files. I'd leave test modules next to prod modules, e.g.:
To me the above example looks cleaner than:
OR:
In the article I mention that I put components and their accordings tests into the same directory.
The "global"
test
directory includes tests that do not belong to certain components :)Thanks for the article it was very helpful. Shazebict Offers Best Support Services for Companies within dubai abu dhabi sharjah uae for all your Software, Hardware, Network, Backup, Recovery, CCTV, Access Control and Accounting Software related IT requirements.
This is super close to what I've started using over the years, its a great structure.
I also create my structure like this, but I create a separate UI folder within components that hold my Card, Buttons and other common components. I skip this part when I use a UI library like Material UI or Chakra UI.
Actually, I also use an UI component folder for more generic components like Buttons for example. I just didn't mention it in the article to keep things simple. Might be a good idea to add it.
It's beginning to look a lot like next.js :d except next has server side code
That's possible, I never used Next.js until now :)
You should really give it a try. As a react developer, I'm sure you'll fall in love with it!! :D
Have fun!
And great article btw, I'm sorry I didn't mention it before :D