React is great. It gives us a ton of freedom to do what we need to do. However, with great freedom comes the increased potential for a project that’s structurally mangled.
When I first started using React, all the tutorials were pretty basic with file structures that were linear and easy to follow. However, what these projects cannot address is the reality of real-world complexities.
Structuring files for a project is not a new concept. Different methodologies have been enforced by frameworks to ensure consistency across different projects. React does not enforce how we should structure our projects.
Here are 5 high-level methods that can prevent your project from slipping into chaos.
Feature-based grouping
Feature-based grouping is a popular way of arranging your React components. This is achieved by grouping your features based on a parent
feature, which then drills down into child
features.
For example, a profile
feature can contain several component groups such as author
, details
, and feeds
. However, this can pose a problem if features overlap across different parent
features.
Pros: Your files are neatly arranged based on features, making their relationship domains and reason for existing clear at first glance. It is also easy to deal with when the project is small and mid-sized. When there is a clear product roadmap, it is also easy to plan in advance what and where components will be placed.
Cons: The issue with this structure is that as the number of features grows, the chances of overlaps occurring increases. This can create inconsistencies or duplicates, if not a file system that doesn’t accurately represent your app.
Atomic design applied
React and atomic design goes well with each other because of their component composing ideologies. Atomic design works on the idea that everything can be abstracted into a singular module, which can then be used to construct bigger compositions of UI entities such as search bars and card groups.
Here is an article that outlines how to implement atomic design on a React project: Using atomic design to create visual cohesion for your React components.
Pros: Focuses on a UI first approach and ensures that you make the design the central piece in the code creation process.
Cons: As the project grows, so do the number of components. This means that general management is still required through categorizations and groups. Also, only work on UI-based code and do not address the data side of your project.
Route based grouping
If you are using React-Router
, then file structure is determined for you by the library.
What is React-Router
? It is a client-side routing library that takes away the need to manually map out your routes in a routing file. Instead, it takes your file structure and pre-determines your routes for you.
So how does route-based grouping work?
Your routes are determined by the file structure, which makes you think about how and what you place inside your components. For example, the route /sales/invoices/$id
will have a folder structure that looks something like this:
sales
-- invoices
-- $id.ts
Pros: Lets you construct composite views easily, meaning that your actual React components are sitting in a different file structure that is better suited for your project’s organizational needs.
Cons: You still need to figure out how to arrange your files for the components if they are not inside the routed files themselves.
File types grouping
File types grouping flips React’s usual separation of concern structure on its side. This method is commonly seen in Angular-based projects, and may be better suited for teams who are used to arranging their code based on file types rather than feature-based. This method is also popular in starter tutorials because it compartmentalizes the different JavaScript work and thinking associated with each file type.
When we talk about file types, we’re not just talking about file endings extensions such as ts
and css
. We are talking about the type of content inside them. For example, API
based files are placed inside an API
folder, while components are placed inside a components
folder.
Pros: Easy to determine the content type inside each folder, making it quick to track and trace when debugging because of the reduced number of parent folders.
Cons: As the project grows in size, so will the number of sub-folders involved. It may also include other styles of file structures, such as feature-based in order to make sure that everything remains organized.
Hexagonal architecture
Hexagonal architecture is complex at first glance but works well for full-stack apps that aren’t just limited to frontend code. The idea of hexagonal architecture was developed by Alistair Cockburn where files are split between four conceptual areas: the domain, the application, the infrastructure, and the UI.
Here’s an example of a hexagonal architecture file system applied:
src
|––__tests__/
|––__mocks__/
|––application/
| |––actions/
|––domain/
| |––app/
| |––enttities/
|––infrastructure/
| |––api/
| |––services/
|––ui/
| |––components/
| |––containers/
| |––hoc/ // optional
| |––hooks/ // optional
| |––screens/ // or "pages"
/
Pros: great for large and complex projects. It also adopts a domain driven design principle in its organizational procedures, which works well for React’s component-based UI composing methodologies. It also accounts for data side of a frontend app and its possibly complex requirements.
Cons: Involves understanding how domain driven design works and may act as a conceptual barrier to new developers who are not accustomed to the idea.
So, how are we supposed to structure our React project files?
Unfortunately, there is no definitive answer to this. In part, it’s because every project will have its own requirements and at different points of the complexity spectrum.
However, due to the component nature of React, it makes it easy to move and rearrange your files. The main of point architecture and structure is that it makes sense for your context. This may change as your project grows, which changes the context of what your app needs to be cohesive and structurally tidy.
Overall, the hexagonal architecture method is the most complex methodology in this piece. It is also the best at dealing with large apps that require finer precision towards organizing something that may span over 100 files. Despite this, it doesn’t mean that it will work for your app’s context.
Over abstraction is often a time sunk cost, if not also an impediment in your app’s architectural organic growth. It is better to start with something simple and then refactor it as needed into a structure that works better. The more you work on your app, the better you will understand its needs and requirements. Following this process flow also lets you clarify and understand the app better against its conceptual form vs. the reality of the code.