How I Like to Organise my React Projects
Published: Aug 1, 2022
React is an un-opinionated library which leaves a lot of choice to the developer. I believe this has benefited the React ecosystem but it also means there are many ways to achieve the same goal. One choice up to developers is how to structure your codebase. I have my personal opinion written down here but I am also open to different ways of organizing a React codebase. (if you disagree with me I can still work for you 🤣)
The React official docs has it's own un-opinionated advice here.
Contents
How I Like to do it
Components/
Layout/
Footer/
Layout.tsx
index.ts
Utilities/
Button/
Button.module.css
Button.stories.tsx
Button.tsx
index.ts
Articles/
Utilities/
TableOfContents/
TableOfContents.module.css
TableOfContents.stories.tsx
TableOfContents.test.tsx
TableOfContents.tsx
index.ts
ReactProjectStructure/
index.ts
ReactProjectStructure.module.css
ReactProjectStructure.tsx
Utilities/
fetchData.ts
transformData.ts
Folder Structure
The layout of the folders uses a hybrid of page based and feature based organisation for my components.
The goal of this layout is to make it easy to find components by using a predictable set of repeatable practices.
The structure of the folders matches the routes of the website, for example, Components/Articles/ReactProjectStructure. The ReactProjectStructure.tsx file then has all the code found on this page. This is page based organisation, any React developer should be able to look at the page route and in turn, find the relevant component quickly.
Utility Components
Even though all the code for this page exists in the ReactProjectStructure component there are a couple of reusable components implemented within the page. These are the FileExplorer component and the TableOfContents component. As both of these components are used in multiple places it doesn't make sense for them to be located in Components/Articles/ReactProjectStructure.
For this reason, these components are stored within a Utilities folder, to avoid having a single Utilities folder that grows massive components are put in the earliest possible parental utilities folder. Or to be clearer in this case the TableOfContents is only used within Articles, therefore, the code is found at Components/Articles/Utilities. Something like the Button component which is used everywhere is found in Components/Utilities.
File Co-location
Co-location of files is important to me. When I am looking at a component I want to be able see its associated styling (CSS), tests and storybook files next to it. This makes it quicker to find associated files when working on a component.
I also find that co-locating the tests with the component makes it very obvious when a component is untested. This can help ensure good testing habits are implemented within a team. Some people still like to think in a clear "separation of concerns" model and have a separate mirrored folder structure for tests and CSS etc. This is also commonly seen in Java and C# projects by default. I dislike this way of organizing the code. When working on a component I want everything relevant to that component right next to it, for me, this is a much better developer experience.
What is that index.ts file about? (barrel files)
Cleaner Imports
The index.ts file is incredibly simple, all it does is import the relevant component and then export it again.
/Components/Articles/TableOfContents/index.ts
1export {TableOfContents} from './TableOfContents'
So if it is not doing anything why do I have it? The index.ts file allows my imports to look "clean". Without the index.ts file importing the TableOfContents component would have to look like this.
1import { TableOfContents } from '../Utilities/TableOfContents/TableOfContents'
While it is fairly minute to care about I dislike the double TableOfContents in the import. ("/TableOfContents/TableOfContents"). Exporting from the index.ts removes this and allows my import to look like.
1import { TableOfContents } from '../Utilities/TableOfContents'
Meaningful File Names
Some of you might be saying that's fine but if you just changed the name of TableOfContents.tsx to index.tsx the imports would still be "clean" and you could remove the index.ts file. Removing the index.ts file could be nice as you might see it as unnecessary boilerplate.
My issue with this approach is that you end up with hundreds of components that are not accurately represented by their filename, worse they all share the same filename index.tsx. This can make searching by file name a less smooth experience.
Below is a screenshot from VS Code on a previous project I worked on before I came up with this system (whoops). I want to quickly go to a component called Location, it should be shown as the first choice. Because the filename is index.tsx it is hidden below our CSS and StoryBook files. This project was also small so the only index.tsx file being displayed is the correct file for the component we want.
As projects grow larger your IDE will find many potential files all called index.tsx. I want to be able to type the component name, hit enter and the code is there, I don't want to be scanning through the file list, looking at the whole path to figure out which one is correct. The screenshot below shows that when the file name represents the actual component it is the first choice shown to the user.