Component composition is the fundamental concept in React that’s essential to learn to become a solid React developer. Newer React devs tend to use inheritance and Context to solve problems that could often be solved more elegantly using composition.
Let’s review the basic principles behind composition in React and brush up on our fundamentals.
Overview of Component composition
Simply put, component composition means building more complex components on top of simpler ones. There are two ways to go about it:
- Specialized components
- Container components
Let’s cover these two approaches in more detail.
Specialized components are more opinionated versions of generic components. In other words, specialized components are usually non-reusable versions of the generic components that we tailored for a specific use case.
We create specialized components by rendering generic ones and configuring the props to match our specific use case.
Let’s see an example where we create our newsletter sign-up form using a generic form component.
We will start with the generic form:
Form is a generic component used to build different forms throughout your app. It’s versatile and lets you configure the inputs for your form, the header, and the submit button.
Now, we will create the specialized
NewsletterForm component on top of our
Identifying patterns that you use a lot throughout your app and turning them into generic reusable components is an important skill to develop.
Once you have a solid set of generic components, it’s much easier and faster to create specialized ones on top of them. That’s why it’s always a good idea to include a UI component library in your new project.
The container component provides certain functionality without knowing its children ahead of time. This kind of component usually has a
children prop that lets you pass any arbitrary content. Good examples of container components are sidebars or dialogs.
The container component is a flexible and versatile pattern. An example I often use in my applications is the gate component that checks the user’s authentication status before rendering its children. Here’s my post that covers creating such a gating component in more detail.
Now, let’s see this pattern in action and create a simple version of the gating component:
Container with multiple child props
In some cases, you might need container components with multiple places to inject your custom content.
One example could be a hypothetical
Dashboard component that accepts header and content components.
When you need to build a container with multiple “holes,” you can break away from the pattern of using the
children prop and add custom props for your injected content.
Why composition is important
By now, you should see that component composition is extremely useful when working with React. Still, let’s list the main advantages of using component composition:
- When building your components on top of the generic ones, you have fewer components that feel like a black box. That, in turn, will improve the transparency in your code and will make it more readable.
- Using intelligent component composition, you can often avoid prop drilling without Context or Redux. More often than not, when you think you need Context to pass props, you need to rethink your component composition.
- Over time, you will build a component toolbox that will increase the velocity of your development. Creating new components will become much faster.
In this post, we covered component composition - the fundamental skill for becoming a solid React developer.
While the component composition is powerful, you can also take it too far and overengineer your app. Not every simple component needs to be generic and reusable.
A good rule of thumb: it’s okay to copy-paste component code two times, but on the third time, you should abstract it into the reusable one.