stefanos.dev

About

React file structure(s): Don't agonize and organize

Cover Image for React file structure(s): Don't agonize and organize
4 min read1191 words

Don't agonize and organize is a common quote when it comes to structural issues among React projects. Being the most famous library without any rules on how to group your files, React projects start to get messy fast. When we start organizing our project's directory most of the time we are overengineering the whole thing or we end up moving files around until we get tired or find the right spot. In React documentation they suggest 2 types of file structuring.

  • Grouping by features or routes

  • Grouping by file type

Our approach will follow the group by file type . For our needs we will use TypeScript. Regardless of the verdict of this article the React file structure(s) is a highly opinionated topic, thus this article is 100% a subjective opinion of mine. I really like the file type grouping for small to midsize projects. Moving to larger projects the use of routes and containers seems a better choice. Still, both will work fine.

Organizing files by type is a common approach since the dawn of React. It’s easy to keep track of everything and when you are ready to scale up it will be a smooth operation. An example of this kind of structure.

api/
├─ APIUtils.test.ts
├─ APIUtils.ts
├─ ProfileAPI.ts
├─ UserAPI.ts
components/
├─ Avatar.css
├─ Avatar.tsx
├─ BlogArticle.test.ts
├─ BlogArticle.tsx
├─ Profile.tsx
├─ ProfileHeader.css
├─ ProfileHeader.tsx
pages/
├─ About.css
├─ About.tsx
├─ Blog.css
├─ Blog.tsx

If we have more than 2 files per feature/page/component then we can nest one more folder:

api/
├─ APIUtils.test.ts
├─ APIUtils.ts
├─ ProfileAPI.ts
├─ UserAPI.ts
components/
├─ Avatar/
│  ├─ Avatar.css
│  ├─ Avatar.tsx
│  ├─ types.ts
├─ BlogArticle/
│  ├─ BlogArticle.test.ts
│  ├─ BlogArticle.tsx
│  ├─ types.ts
├─ Profile.tsx
├─ ProfileHeader.css
├─ ProfileHeader.tsx
pages/
├─ About.css
├─ About.tsx
├─ Blog.css
├─ Blog.tsx

Api/Services folder

The API, or services folder is not always mandatory for our application. It's created when we use a more complete service. Inside we can keep our store, our requests or a library for our state management (i.e redux).

Assets folder

Assets folder, it will contain assets of our project. It consists of images, icons or fonts. In many cases you will find there the global styling of the web app and/or the theme.

Components folder

Components are the building blocks of any react project. This folder consists of a collection of UI components that can be used across various files in the project. Components folder can have a multipurpose in our project. Some people keep both their global and pages/routes components here.

components/
├─ Avatar/
│  ├─ Avatar.css
│  ├─ Avatar.tsx
│  ├─ types.ts
├─ BlogArticle/
│  ├─ BlogArticle.test.ts
│  ├─ BlogArticle.tsx
│  ├─ types.ts
├─ PrimaryButton.css
├─ PrimaryButton.tsx
├─ Profile.tsx
├─ ProfileHeader.css
├─ ProfileHeader.tsx

Pages/Routes Folder

The files in the pages folder indicate the route of the react application. Each file in this folder contains its route. A page wraps the react components and its services. A page can contain one or more subfolders (depends on the occasion).

pages/
├─ Blog/
│  ├─ Blog.css
│  ├─ Blog.tsx
│  ├─ Blog.test.ts
│  ├─ types.ts
├─ About/
│  ├─ About.css
│  ├─ About.tsx
│  ├─ AboutFooter/
│  │  ├─ AboutFooter.tsx
│  ├─ About.test.ts
│  ├─ types.ts

Utils Folder

Utils folder consists of some repeatedly used functions that are commonly used in the project. It should contain only functions which will provide us with a specific result. For example, Regex condition, data formatting, file validator etc.

utils/
├─ objectIsEmpty.ts
├─ DateFormat.ts
├─ string2Bool.ts
├─ capitalizeFirstLetter.ts

Helpers Folder

Helpers folder often takes the place of Utils folder but most of the times it contains functions that will transform/normalize our data.

Extending this approach

Taking this a bit further and adding a more personal touch, we can create three more folders inside the src directory.

  • modules
  • core
  • scripts

The modules folder

The Module folder has a unique place in our structure. It contains the global components which consist of both UI and business logic. For example, if you create an Uploader or a task manager, both of them are two building blocks completely autonomous consisting of their own services, reducers etc. then the modules folder is a good place to have them.

The 'core' of the project

Core folder truly represents its definition. It's the core of the project. Here you can add the Page/Layout component which will work as a wrapper for most of the files or the Error Report component using React's built in Error Boundary. Most of the utils/helpers can be found here such as functions for date formats, string to boolean, capitalize the first letter etc.

Scripts Folder

The Script folder has its own place when we have to deal with multiple webpack configuration files, docker-compose or Dockerfiles. Any file that makes our app running is more than welcome to put it in this folder. Scripts folder helps to keep a clean source directory and separate our configuration from the rest of the files.

modules/
├─ Alert/
│  ├─ Alert.tsx
│  ├─ reducer.ts
│  ├─ sagas/
├─ Upload/
│  ├─ Upload.tsx
│  ├─ reducer.ts
│  ├─ sagas/
scripts/
├─ webpack.dev.js
├─ webpack.env.js
├─ webpack.prod.js
├─ webpack.config.js
core/
├─ Page/
│  ├─ Aside/
│  │  ├─ PageAside.tsx
│  │  ├─ PageAside.styled.tsx
│  ├─ Header/
│  │  ├─ Header.tsx
│  │  ├─ Header.styled.ts

Avoiding index.ts

Regardless of the approach you will choose to follow, one thing is certain. Do yourself a favor and don’t name every file index. Very often you will find index.ts in projects to act as an entry point for a particular folder. In large scale projects this can be a pain. Indirection is the most common scenario. Underneath this you can unveil many issues that can reduce your team’s performance. For example, debugging using dev tools can turn out to be your worst nightmare. There’s nothing worse than looking at your console/editor trying to find a file when everything is called index.ts.

Choosing a correct naming for your files can make a project more easily searchable across the codebase and helpful when editing multiple files to avoid confusion. For example keep the Header.tsx as your main Header file instead of index.ts and you are good to go.

Disclaimer: In many cases I use index.ts as an exportation file for my components in order to have a better importation experience. For example:

pages/
├─ Blog/
│  ├─ Blog.css
│  ├─ Blog.tsx
│  ├─ Blog.test.ts
│  ├─ types.ts
│  ├─ index.ts
├─ About/
│  ├─ About.css
│  ├─ About.tsx
│  ├─ AboutFooter/
│  │  ├─ AboutFooter.tsx
│  ├─ About.test.ts
│  ├─ types.ts
│  ├─ index.ts

In this case the index.ts file will look like:

import AboutPage from './About';

export default AboutPage;

Importing the AboutPage inside the Blog will change from this:

import AboutPage from '../About/About';

to this:

import AboutPage from '../About';

Test folder ( Alternative method )

As your project grows restructuring the test files should be in your refactoring list. Creating a folder _tests_ inside each page/route/component folder can have a big impact.

pages/
├─ Blog/
│  ├─ _tests_/
│  │  ├─ Blog.test.tsx
│  ├─ Blog.css
│  ├─ Blog.tsx
│  ├─ types.ts
├─ About/
│  ├─ _tests_/
│  │  ├─ About.test.tsx
│  ├─ About.css
│  ├─ About.tsx
│  ├─ AboutFooter/
│  │  ├─ AboutFooter.tsx
│  ├─ types.ts

Conclusion

Grouping the files by type is my favorite approach so far. It’s flexible and scalable. It offers everything a developer needs and it’s easy to move forward while scaling the project. Organizing by type provides a clean structure separating the components from the route and creating a more friendly experience for a newcomer. Furthermore, you can extend this approach to whatever you feel will help your team. Be careful of the deep nesting and everything will go smoothly. Choose your approach with your team. Every team works differently and every project has different needs. If this approach doesn’t suit your team/organization it’s definitely ok to try something else.