React folder structure for enterprise level applications

Kolby Sisk
Udacity Eng & Data
Published in
4 min readNov 29, 2021

--

Photo by David Bruno Silva on Unsplash

Structuring a project’s folders in a manner that fits the project’s size and intent is key to scalability and maintainability. Unfortunately there aren’t many examples that document a structure that works well for enterprise level applications. There also is no single correct answer for what a perfect folder structure looks like. The following structure is one that I’ve been evolving for many years and have used in many enterprise level projects.

The goal of this article is to explain the reasoning behind this structure so that you can use these ideas to architect the best solution for your project.

TL;DR

Before I explain the intent for each of these directories, let me explain the methodology behind this approach.

  • Composability allows you to reuse code between components. This is a very powerful concept in React, and a large influence as to why it’s common to break-out and group reusable code.
  • Local first. When building components it’s best to keep the code that pertains only to that component, local. In our example above, the authentication Feature Component contains an auth-form component, and some utils. Instead of breaking those out into their respective components or utils directory, we keep them local because the code they contain will never be used in another component. This will help prevent those directories from becoming unreasonably large as the project scales.
  • Flatter is better. Every time you nest you’re adding to the mental model required to grok a component’s local code. Nesting also comes with other pain points, so it’s best to avoid too much nesting.
  • Nest as you grow. Flatter may be better, but there are reasons to nest. For example if your Features directory grows too large, you might want to group your features by something logical.

components/

  • Contains reusable components that are most often used to compose Feature or Page components.
  • These components are almost always pure and presentational, with no side-effects.

constants/

  • Contains reusable & immutable strings like URLs or Regex Patterns.
  • A single index file will suffice for most projects.

contexts/

  • Contains reusable contexts used to provide data through a tree of components.
  • If using Redux this directly is likely not needed.

features/

  • Contains reusable Feature Components. A Feature Component is a concept inspired by Redux in which all logic required for a feature is colocated to a single directory. A Feature Component is often composed of many other components, either local or shared. The same is true for all resources: utils, types, hooks, and so on.
  • Feature Components often include side-effects.
  • If using Redux, and interacts with the Store, the Feature Component will include a slice file that defines the “slice” of the Redux Store the feature represents.

layouts/

  • Contains reusable Layout Components. A Layout Component is a component that composes the layout of a page. It will often import components such as app-bar, app-footer, app-side-nav.
  • If your project is likely to only have a single layout, this directory might not be necessary, and the Layout Component can live in the components directory.

hooks/

pages/

  • Contains Page Components. Each Page Component is associated with a route.
  • Page Components compose the content of a page by importing Components and Feature Components.
  • A Page Component should rarely include side-effects, and should instead delegate side-effects to Feature Components.

services/

  • Contains reusable code for interacting with an API, often in the form of hooks, and ideally utilizing a server-cache tool like React Query or RTK Query.
  • Do not mistake this with an Angular-esque service which is meant to handle injecting functionality into components. React handles this scenario using contexts and hooks. This directory should only contain code for interacting with an API.
  • Inspired by RTK Query’s recommendation to keep your API definition in a central location. This is the only example of where we purposely break the local-first rule. I like to think of API definitions as their own modular feature. In fact it’s not uncommon to have a features/api directory in lieu of a services directory.

“Our perspective is that it’s much easier to keep track of how requests, cache invalidation, and general app configuration behave when they’re all in one central location in comparison to having X number of custom hooks in different files throughout your application.” — RTK

styles/

  • Reusable or global styles.
  • Might include configurations, resets, or variables.

types/

  • Reusable types for projects utilizing TypeScript or Flow.
  • I often put my reusable Zod Schemas in this directory as well, and export the inferred type.

utils/

  • Reusable utility functions.
  • These functions should always be pure and produce no side-effects.

store.ts

  • If using Redux, this file is where you import all of your slices and configure your Redux Store.

Pro Tips

  • Read official documentations every time you start a new project. You never know what new tool a library may have added since you last implemented it.
  • Stop using PascalCase for file names. Instead use kebab-case for all files.
  • Use a server-state tool like React Query or Redux Toolkit Query.
  • Use a component library like MUI.
  • Use a hook library like usehooks-ts for common hooks.

--

--

Builder of software with a passion for learning. I specialize in web development and user experience design. www.kolbysisk.com