Colocating React component files: the tools you need

David Barral
May 27 · 4 min read
Photo by on

React does not force any file structure to work. File structure is one of those themes up for debate where no choice can be the gold standard.

In general, it is a good idea to keep files that often change together close to each other. This principle is called “colocation”. ()

This post is not about why you should or shouldn’t colocate. That’s up to you. We do colocate our component files. That’s our choice. So, this post is about how we do it and how we configure our tools to support it.

Our current component layout

We put each component in its own folder. This folder is named after the component using kebab-case. is not relevant. I won’t debate whether is best to use kebab, snake, pascal, camel or any other casing you may love. Component folders are arranged inside other folders to define a taxonomy.

Each component folder contains several files, also named after the component. Each one with its own distinctive extension:

  • .jsx for the component’s code. The component is always the default export.
  • .module.css for the component styles. We use and .
  • .test.jsx for the component’s tests. is our testing framework of choice.
  • .test.jsx.snap for the test snapshots.

We also add an index.js file that re-exports the component to have shorter import sentences: import Component from "path/to/component"; instead of import Component from "path/to/component/component.jsx";.

Example, a simple button component

Our SimpleButton will be grouped under buttons and has the following layout:

components
└── buttons
└── simple-button
├── index.js
├── simple-button.jsx
├── simple-button.module.css
├── simple-button.test.jsx
└── simple-button.test.jsx.snap

simple-button.jsx defines the component. In this example a SimpleButton that can be either enabled or disabled:

simple-button.module.css contains the CSS modules for the component. Selectors follow :

The index.js re-exports the SimpleButton component from simple-button.jsx:

And, lastly, simple-button.test.jsx contains the Jest test suite for the component. Notice we import SimpleButton from ".". This way we make sure the index.js re-export is working as expected.

Colocation, following this layout, gives us several advantages:

  • Component files are easy to find.
  • Trivial imports from test to component, component to assets, etc.
  • Easily change the components taxonomy. To move a component, just move its folder around.
  • Easy extraction of components to their own packages. Once again, you just need to move the folder around.

Depending on which tools we are using to document our components we can have more colocated files (.md or .mdx for example. We use both and , depending on the project). We are not covering this topic today, but it also benefits from colocation.

Configuring the tools to fit this layout

Your everyday JS tools does not encourage colocation by default, or, at least, not this way. Don’t worry, making them work the way we want is just a matter of tinkering with the config.

Babel: allowing default exports

The current standard syntax for export does not allow to re-export from another package the way we do in our index.js. It needs more boilerplate code unless you use Babel and . It implements the to support the export from syntax.

Webpack: supporting CSS modules

To use CSS modules, webpack must use the enabled. We setup the loaders for the .css imports overriding the .module.css imports to use CSS modules. This way we can import some third party stylesheet or setup some global styles avoiding the CSS modules scoping.

Jest: collocating test and snapshots

By default Jest will look for your component test in a __test__ folder and store snapshots in __snapshots__. You can change this behaviour using the and config options. With testRegex you state that your test will be named after the component file and use a .test.jsx extension.

The custom snapshotResolver tells Jest to store the snapshots in the same folder as the test, adding a .snap extension.

Jest: supporting CSS modules

If you use CSS modules you must tell Jest how to handle your .css imports by configuring a .

We use . The styles you import in your component will be an ES2015 that returns the keys you use as a string: styles.Button will be "Button". This way the classNames in the snapshots will be meaningful.

ESLint: applying rules only to test files

Last but not least, we use to check our code. We can apply certain linting rules only to our test files using the option. We use it to enable Jest globals and setup some rules.

Summing up

Colocating component files brings several advantages. We strongly believe that it is the better approach and we are happy with it. You can develop your componentes following this layout too, just by making simple changes to your tooling configuration. Give it a try. You won’t go back.

Trabe

We are a development studio. We use Java, Rails, and JavaScript. This is where we write about the technologies we use at Trabe.

Thanks to Asís García and Lucas Andión.

David Barral

Written by

Co-founder @Trabe. Developer drowning in a sea of pointless code.

Trabe

Trabe

We are a development studio. We use Java, Rails, and JavaScript. This is where we write about the technologies we use at Trabe.