How to name and organize your React components

When I first began building complex React applications, I struggled to determine the best way to name and organize my components within my codebase. Having come from the Rails school of convention over configuration, I was perplexed to find that most modern JavaScript apps rely heavily on custom configuration and don't adhere to any sort of community-driven conventional norms. That's changing slowly with the advent of toolkits like create-react-app, but even its conventions go right out the window as soon as you npm eject.

After a couple years of learning and mistakes, there are a few guidelines I use when organizing my React components so my code is more readable, understandable, and succinct.

Compose your components into smaller subcomponents

While it's tempting to just keep adding bits and pieces to your component's render method, this can grow to the point where it becomes difficult for new eyes to discern your intentions. Imagine a render method like this:

render() {
  return (
    <div className="todo-list">
      <div className="todo-list__items">
        {this.props.items.map(item => (
          <div className="todo-item">
            {item.title}
          </div>
        ))}
      </div>
    </div>
  );
}

Instead of rendering todo items within the root component's render method, create a new component:

const TodoItem = props => (
  <div className="todo-item">
    {props.item.title}
  </div>
);

Then, call it in our root component's render method:

render() {
  return (
    <div className="todo-list">
      <div className="todo-list__items">
        {this.props.items.map(item => (
          <TodoItem item={item} />
        ))}
      </div>
    </div>
  );
}

It's a subtle change, but making these sorts of changes proactively can keep your components readable. And becuase React only re-renders those components that change, extracting smaller components can improve your application's performance.

Extract iterators into class methods or new components

Extracting a subcomponent is a good first step. But we can go one step further by extracting the map iterator call into its own method in the root component. This improves the readability of the render method:

render() {
  return (
    <div className="todo-list">
      <div className="todo-list__items">
        {this.renderItems()}
      </div>
    </div>
  );
}

renderItems() {
  return this.props.items.map(item => (
    <TodoItem item={item} />
  ));
}

Now when we scan the render method, we see a more succinct summary of its contents!

Only use ES6-style class components where state is needed

Notice in my previous example that I've opted to use const to define the TodoItem component. This is because, in its current incarnation, the TodoItem component is stateless.

In React, stateless components are merely functions that return a React-wrapped DOM element. Unlike using the ES6 class keyword or the now-antiquated React.createClass method, stateless components cannot hold their own component state in a this.state object.

The reason this syntax is favorable is because it encourages authoring code in a functional style. Let's expand our TodoItem component to include a "Delete" button:

const TodoItem = props => (
  <div className="todo-item">
    <div className="todo-item__title">
      {props.item.title}
    </div>

    <button onClick={props.onDelete}>
      Delete
    </button>
  </div>
);

Here we've enclosed the title in a new container div, and then added a <button> tag with an onClick handler set to a hypothetical onDelete thunk handler that we would pass through from the parent component.

Just like in our iterator extraction example above, there's opportunity to make this component more readable. However, because this component is stateless we can use a more functional style:

const Title = props => (
  <div className="todo-item__title">
    {props.value}
  </div>
);

const DeleteButton = props => (
  <button onClick={props.onDelete}>
    Delete
  </button>
);

const TodoItem = props => (
  <div className="todo-item">
    <Title value={props.item.title} /> <DeleteButton
    <DeleteButton onClick={props.onDelete} />
  </div>
);

Our goal in doing these extractions is to reduce fatigue when scanning your components' code. New developers who visit this code for the first time will be greeted with components whose markup is only a few lines, making it much easier to parse and understand than if they were tens or hundreds of lines.

Keeping this habit early on will mean your codebase can grow and remain understandable to newcomers.

Organize composed components into subdirectories

So far, we've built the following components:

  • TodoList
  • TodoItem
  • Title
  • DeleteButton

As our codebase grows, the way we physically organize our code on disk is going to become more critical. I've experimented with a couple methodologies.

The first is to create a directory called components, dump all your components in there, and call it a day. This is fine for projects with fewer than 20 or so components, but it becomes cumbersome as the number of components grows.

Instead, I've settled on creating subdirectories for certain root-level components. In our example, we could envision the following directory structure:

  components/
    TodoList/
      index.js
      Item/
        index.js
        Title.js
        DeleteButton.js

Organizing our components in this way has the following benefits:

  • Components only ever reference other components within their own subdirectory.
  • We can name components based on their context. For instance, instead of naming our item component TodoItem, we can call it Item. This reduces unnecessary verbosity.
  • Our components become portable. By encapsulating their hierarchy within a single directory, we can reuse the component in other codebases easily.

Want help organizing and building your React-based project? I'm currently accepting new clients! Get in touch and tell me about your project.

Hiring a web developer? Read this first

You're about to build a new web application, but you're terrified at the breadth of terminology and wary of consultants nickel-and-diming you.

My free book Why Software Projects Fail offers that framework. In this companion to your hiring and discovery process, you'll learn how to inform your next decisions and to empower yourself along the way.

In the book, you'll learn:

  • How to find and hire a trustworthy consultant
  • Why it's critical you pay for a software discovery
  • How to assess your consultant's bid
  • What to expect—and be wary of—during the development process
  • How to take control of your project

Enter your email address below and then click the "Send Me My Free Gift" button. I'll send you Why Software Projects Fail, and you'll be equipped for success on your next project.