Dan Abramov famously officialized the file structure for React applications as “move files around until they feel right.” I do not want to disagree with this point. I agree wholeheartedly. However, despite Dan’s advice, the question of optimal file structure still gains traction frequently. Despite absolute freedom, developers are still uncomfortable with exploring new territories; and I think they have a point. It’s a lot of work to refactor a code base for a file structure change, and it takes a lot of trial and error to find one you like. It would be beneficial to know some ground rules before mapping out your expedition — what have those who came before you discovered?

Over the past three years, I’ve gained extensive experience using React from personal projects to corporate settings; from teams of 2 to teams of 16; from React 0.12 to React 16.10; from Redux to ReactN; from the browser to React Native. No matter the context of the React application, the structure “feels right” under all of these conditions.

This article is an opinion piece for what file structure has worked best for me and my teams after our own trial and error. You are more than welcome to adjust it for your own use case. I am interested in maintaining this article as a living document of what has worked best for the React community, under what conditions, and why. Please share your file structure discoveries in the comments below. 💬

To see this file structure in a living application, I have also created an accompanying GitHub repository.

Create React App 👷🏾‍♂️

I am a huge fan of the create-react-app command. It requires hardly any changes out of the box, and its popularity makes for a foundation recognizable to many developers.

This is the file structure created by create-react-app as of 2.1.5 :

my-app

├── build

├── node_modules

├── public

│ ├── favicon.ico

│ ├── index.html

│ └── manifest.json

├── src

│ ├── App.css

│ ├── App.js

│ ├── App.test.js

│ ├── index.css

│ ├── index.js

│ ├── logo.svg

│ └── serviceWorker.js

├── .gitignore

├── package.json

└── README.md

This is a solid start.

build is the location of your final, production-ready build. This directory won’t exist until you run npm build or yarn build . The contents of this folder should be ready-to-ship without any interaction on your part.

is the location of your final, production-ready build. This directory won’t exist until you run or . The contents of this folder should be ready-to-ship without any interaction on your part. node_modules is where packages installed by NPM or Yarn will reside.

is where packages installed by NPM or Yarn will reside. public is where your static files reside. If the file is not imported by your JavaScript application and must maintain its file name, put it here. Files in the public directory will maintain the same file name in production, which typically means that they will be cached by your client and never downloaded again. If your file does not have a filename that matters — such as index.html , manifest.json , or robots.txt — you should put it in src instead.

is where your static files reside. If the file is not imported by your JavaScript application and must maintain its file name, put it here. Files in the directory will maintain the same file name in production, which typically means that they will be cached by your client and never downloaded again. If your file does not have a filename that matters — such as , , or — you should put it in instead. src is where your dynamic files reside. If the file is imported by your JavaScript application or changes contents, put it here. In order to make sure the client downloads the most up-to-date version of your file instead of relying on a cached copy, Webpack will give changed files a unique file name in the production build. This allows you to use simple, intuitive file names during development, such as banner.png instead of banner-2019-03-01-final.png . You never have to worry about your client using the outdated cached copy, because Webpack will automatically rename banner.png to banner.unique-hash.png , where the unique hash changes only when banner.png changes.

From here, create-react-app gives us total freedom. I’ll walk through the file structure that has worked the most cooperatively with the projects on which I’ve worked.

Tweaking Create React App 🔧

The immediate two issues I find with create-react-app 's output are:

Files should be separated into directories by type.

File names should be lowercase.

One of the most important and agreed upon structures for a React project is to have a components directory for storing your components. Some developers use two directories — one for stateful components and one for stateless components. As an opinionated article, I’ll discuss what I’ve found most intuitive: a single directory for components. Keep related code as close as possible. That is where we’ll move App.js and its siblings.

src

├── components

│ └── app

│ │ ├── app.css

│ │ ├── app.js

│ │ └── app.test.js

│ └── index.js

├── images

│ └── logo.svg

├── index.css

├── index.js

└── service-worker.js

With this new structure, the files directly under src are the entry files. This is where Webpack starts. What is your project? It has a base style sheet, it includes a service worker, and it renders a React application to the DOM.

Your React components can now be found in the components directory. We’ll discuss the src/components directory in more depth later.

You may have noticed I added a src/components/index.js file. This file serves as a “barrel” through which all sibling components are exported. export { default as App } from './app'; . This index.js file will be a crucial part of the testing experience.

Your assets, which are dependencies shared by your application — such as SASS mixins, images, etc. — can go in their respective directories. This provides a single location for updating dependencies used app-wide. If your branding needs to change, the colors can all be adjusted in the centralized styles directory, your banners can all be adjusted in the centralized images directory. If you need a new build for each localization supported, all the language files can be stored in the centralized locales directory.

Depending on your team structure, it can be a big convenience to group these related, non-JavaScript files together in your otherwise JavaScript project. As a JavaScript developer on a JavaScript project, the management of these non-JavaScript resources may be handled by others — graphic designers, translators, accessibility consultants, or public relations representatives. Keeping the files close allows them to easily be dropped in and out and ensures that no file is left behind in a process to override a dated dependency, such as the migration from one colored theme to another or the localization from original English to newly-supported Spanish. Any asset directory that will be imported via JavaScript should have its own index.js file, as shown with the src/components directory.

The project needs another important addition: a utilities directory, such as src/utils or src/helpers . This is a folder full of helper functions that are used globally. Keep your code DRY (Don’t Repeat Yourself) by exporting repeated logic to a singular location and importing it where used. Parts of your application can now share logic without copy-pasting by placing shared logic in this utilities directory. This directory can go by any name, and I’m not picky about it. I don’t think it makes sense to say that one name is better than the others, but it is a directory that needs to exist. I’ve seen many different names used in enterprise settings; what matters is that your team agrees that the name makes sense for your project. Since the utilities folder will be imported via JavaScript, it should contain an index.js file that re-exports its siblings. The testing implications of this index.js file are discussed in Writing testable React components with hooks.

Finally, the project needs a directory that represents standalone modules that can hypothetically be exported from the project entirely. These modules do not contain business logic related to the application itself. Think open-source code that isn’t open-source yet. Ask yourself, “Can another team benefit from this code?” Like utilities, you may call this directory whatever makes sense for you and your team, such as src/open-source , src/packages , src/shared , or src/standalone . Example modules include home-rolled translation systems, useful implementations of complex algorithms and data structures, or highly reusable components that may offer valuable functionality to other teams. For CloudWatch Logs, our team extended Airbnb’s react-dates component in order to add a time selection in addition to its built-in date selection. This new component was standalone and offered value to other teams. While we developed it in the standalone modules directory, we were able to strip it from the project entirely with minimal effort once we had fully extended, tested, and vetted it. It is now used by other AWS teams, and we receive the benefits of those teams’ feature contributions and bug fixes. Unlike src/components and src/utils , we do not give this directory an index.js file, because we want to fully mimic imports as if it were an external package ( import Component from 'open-source-package-name'; and import Component from 'src/open-source/package-name'; ).

Our repository now looks something like this:

my-app

├── build

├── node_modules

├── public

│ ├── favicon.ico

│ ├── index.html

│ └── manifest.json

├── src

│ ├── components

│ │ ├── app

│ │ │ ├── app.css

│ │ │ ├── app.js

│ │ │ └── app.test.js

│ │ └── index.js

│ ├── images

│ │ └── logo.svg

│ ├── packages

│ │ └── ...

│ ├── utils

│ │ ├── ...

│ │ └── index.js

│ ├── index.css

│ ├── index.js

│ └── service-worker.js

├── .gitignore

├── package.json

└── README.md

The Component Directory 🍰