Having a concrete project structure often leads to brittle code. Having a flexible project structure accepted by the community can have many merits. Let’s quickly review.

Advantages of a Project Structure

Teams don’t have to argue on project structure, instead they can focus on building the product.

Since react has a huge ecosystem, there are so many decisions to make, redux or mobx? HOC or render prop? react-motion or react-spring? function component or class component? Having a convention for project structure is one less decision to make.

Consistent structure across projects helps people get acquainted with the project quickly. A quick peek and we can guess what each folder will contain, lowering the learning curve for new team members.

People with less experience can also build scalable projects.

Code sharing + reuse is increased as a more modular structure is created.

Based on the hundreds of requests for standardizing the project structure it’s obvious there is a real need for it. To fulfill the demands of users, Dan Abramov shared his guide.

See here

And the guide says this.

This is really helpful. Though I think when people were requesting a project structure guide, they were expecting a lot more details.

With this article I’ll be sharing the conventions I follow, their advantages and the reasons behind each one of them.

Consistency is key. If you stay consistent then no matter what convention you follow, you’ll probably be able to scale your code. — Aditya Agarwal (Me :)

5 Goals of project structure

Productivity should be increased. Fuzzy searching files in text editor must be easier. Moving files should be effortless. Flexible and not too highly dictative. There should be some room for developers to do their own thing. Structure should encourage scalability, but also reusability. Structure should be simple enough for new team members to quickly get onboard and dive into the project.

The project structure

Component’s file name should be in Pascal Case.

Component names should be like TabSwitcher and not like tabSwitcher, tab-switcher etc. Also no other type of file should be in Pascal Case. This way when we see a filename in Pascal Case, it is immediately clear that the file is a react component.

Avoid default export

Default exports are a special case of named exports and because of that they have to be treated in a special way. It is a common cause of confusion. We can avoid all that by just sticking with named exports.

A component can then be imported like

import { TinyButton } from './shared/Button'

All components should be inside components directory

Keep components inside the dedicated components directory. Keep each component in a separate file. If you need to have styles etc. for the component then create a folder for the component. In the example, TinyButton is a component which needs it’s own custom styling. So we move the component in it’s own folder.

The major advantage is that now the components are not hidden deep inside somewhere. Since they are meant to be reusable they should be discoverable.

Combined with tools like Bit, this create a much faster development workflow for sharing and developing components in different projects.

Components having own folder should have a component file with the same name

If a component is placed in a separate folder, then the name of the folder and the name of component file should preferably be identical. This way when we search for files, we don’t get a list of index.js but the actual component files.

To avoid having to import components like this

import { TinyButton } from './TinyButton/TinyButton'

make an index.js in that components folder which export the named component.

// components/TinyButton/index.js export * from './TinyButton'

Components which can be used in other projects can be kept in shared sub-directory (optional)

Some components are not project specific. Such components can be used across projects and having more such components is highly encouraged. Why reinvent the wheel, when you can use the wheel and build that car.

Use Bit to build apps faster with components. It helps you share and reuse components, with your team, and use them to build new apps!

With Bit, we can easily share these components with teams so they don’t have to reinvent the wheel. I’ll demonstrate how to do this in the end. Note that Bit doesn’t really “care” where the component is located in your file system.

Imagine the next time you have to implement an image carousel and you don’t.

React spinners with Bit: Choose, Play, Install

Routing logic go in pages directory

Inspired from the NextJs project structure convention, the routing logic is kept inside pages directory. Each page is a react class component having some state. A page component uses other components to assemble the page like lego blocks.

The reason is that route logic is mostly non reusable, means page components are not reusable but we want the components inside components directory to be reusable. So it is better that page components are inside pages directory.

Advantages —

Quickly see what routes are available in the app.

Central location for all routing logic.

Keeping page components separated from other components leads to less crowding.

Filename for Page Component should be lowercase

The page name corresponds to the route name. Route names in url are lowercase, it makes sense to have the file name lowercase also.

Some general guidelines

The following guidelines are not related to a project structure and can be used in any react project.

Go with functional components by default

Start out with functional component. Keep the state in the parent. If this scales out of hand then move to class component. See more:

Use Presentational and Container component pattern

Presentational components are those who don’t have internal state. Their role is to show certain pieces of UI. They are provided data via props or context api.

Container components are those which deals with business logic. They usually have some state and only render presentational components based on the logic.

This way Presentational and Container components complete the puzzle together. By dividing the responsibilities, code becomes easier to maintain and debug.

Keep components shallow

If a components has a lot of nested markup then the chances of reusing it decreases. Instead we should take advantage of composition. It saves us from prop drilling or having to reach out to context api. Learn more about it here.

Using Bit to share cross project components

Bit is a great tool for sharing react components across projects. Let’s see how to use Bit to share our react components present in shared directory.

Install Bit and then login to bit by executing this command

$ bit login

2. Then make your react project a bit workspace by doing this.

$ bit init

3. Add your shared components like this.

$ bit add src/shared/Button.js --id mycomps/button

4. Import build and test environments as needed.

5. Put a tag on the shared component by doing this.

$ bit tag mycomps/button

6. Open this link and create a scope named mycomps.

7. Now export the shared component to the scope like this.

$ bit export your-bit-username/mycomps

Congrats!! Your component is now exported on Bit, and you can use it in any project, develop it from different projects and even sync cross-changes. Combining this workflow with a scalable and modular project structure can help boost code-sharing, simplify maintenance (change once, update anywhere) and speed your development velocity.

Conclusion

This short tutorial is my own opinionated idea of some principle guidelines to follow in order to structure a React project which achieves desired goals. Please feel free to comment, suggest ideas and add your own insights!

You can also follow me on Twitter and Medium or subscribe to my newsletter!