Conventions for a React app’s directory structure

What I find important when considering the directory structure for a React project:

  • I want to write code fast, so it must be easy to remember where the files are located and to decide their names
  • Other developers should understand or adopt such conventions without thinking too much
  • refactoring must be errors-free: in React refactoring happens often, for example one specific component becoming a shared component, an API resource being renamed, etc.

I’ve found the best solution is to separate my code by domain in only one subfolder:

app/
├── account/
├── pages/
└── utils/

They are either application-related domains, such as account, products, subscriptions, orders, or code specific domains, like utils, reducers, actions, ui (user interface), pages (what goes into the router) — in any case no abstract distinctions like containers or components.

There is nothing wrong having a directory with many files. Also, I want avoid complex imports statements:

import 😱 from '../../../../stuff/foo/things/MyThing'
import 😊 from '../things/MyThing'

…so I can move files around the app without fixing the relative imports.

Finally my apps structure always looks like this:

app
├── actions
│ ├── orders.js
│ ├── orders.test.js
│ ├── subscriptions.js
│ └── subscription.test.js
├── reducers
│ ├── orders.js
│ ├── orders.test.js
│ └── subscriptions.js
├── store
│ └── configureStore.js
├── account
│ ├── AccountForm.js
│ ├── AccountForm.scss
│ └── AccountDetails.js
├── orders
│ ├── OrderPreview.js
│ ├── OrderPreview.scss
│ └── OrderDetailsModal.js
├── subscriptions
│ ├── ChangeShipmentModal.js
│ ├── ChangeShipmentModal.scss
│ ├── ChangeShipmentModal.test.js
│ └── SuspendSubscriptionButton.js
├── pages
│ ├── AccountPage.js
│ ├── OrdersPage.js
│ └── SubscriptionPage.js
├── ui
│ ├── Modal.js
│ ├── Modal.scss
│ ├── Modal.test.js
│ ├── Button.js
│ ├── Form.js
│ └── LinkToModal.js
├── utils
│ ├── dateUtils.js
│ └── RequiresLogin.js // high order react component
├── server
│ └── middlewares.js
├── client.js
└── server.js

The domain appears often in the component’s name in case it would be too generic otherwise (e.g. AccountPage vs. Account). It also helps to use the same similar suffixes, such as -Pages, -Preview, -Modal, -Button

Only heavily shared components (such as Modal and Button) deserve the right to have more generic names: they look nicer (<Button/> vs. <UIButton/>).

For example, I recently had to rename a /orders/LinkToOrderDetailsModal.js component to /ui/LinkToModal.js . That component was creating a link to open the order’s details, so it was placed into orders. Later, I’ve found it useful for opening other modals as well — so after updating the code a bit, I’ve given to it a more generic name and moved it to another directory. Refactoring has been a simple search and replace because I was sure there weren’t other things named LinkToOrderDatailsModal. Tests and linter would have caught missed imports, anyway.

Other smaller rules:

  • strictly one CSS file per component: if I am not using CSS modules, I use BEM-syntax for class: MyComponent.csscontains .MyComponent {} .MyComponent-container {} .MyComponent-container--active {}selectors
  • I name unit tests as the same file I am testing, plus the .test.js suffix
  • I try to name things according the REST API conventions. This facilitate the cooperation with the API developers :)
Like what you read? Give gpbl a round of applause.

From a quick cheer to a standing ovation, clap to show how much you enjoyed this story.