React Component Composition Explained with Example

In React, the technique of accessing the properties of one component from another component is called React Component Composition.

With Composition, you can create parent-child relational components. It provides the ability to combine small components. And transform into a more functional component. Which will provide more advanced features to your project.

One of the most useful aspects of React component composition is component reusability. Imagine a button that you need to use multiple times in different places throughout your project. With the help of composition, you can create the button component once and use it whenever you need. You don’t have to code every time.

This will save your project development time. And will help to make the project more organized.

There are many similarities between React component composition and component inheritance. But composition is a more modern and better technique than inheritance. Also, the React official team suggests using composition instead of inheritance.

A Simple Example of React Component Composition

Let’s look at a simple example of a composition. First, we will create a simple parent component where we will define a text color property. Then from our main React App, we will call the color property from the parent component.

Create A Parent Component

First, we created a file called “ParentComponent.jsx” in our React project folder.

After creating the file, copy and paste the following code:

//Paste this code into the ParentComponent.jsx file
function Parent(props) {

    return <h1 style={{color:"green"}}>{props.children}</h1>

}

export default Parent

A simple React functional component named “Parent” has been created in the above code. Where the properties of this component are declared through “Parent(props)“.

After that, we simply returned an “h1” tag within the component. The color property of the “h1” tag is defined as “green“.

Now inside the “h1” tag “{props.children}” means the child component of this Parent component will inherit the “h1” tag and “green” color property.

Lastly, we export the “Parent” component so that we can import this component from another file.

Create A Child Component

Now we will create a children component in the “App.jsx” file. Which will call the properties of the parent component.

//Paste this code into the App.jsx file
import Parent from "./components/ParentComponent";

function Children() {

  return <Parent>Hello KingsCoder!</Parent>

}

export default Children

first of all, we import the “Parent” component. So that we can access the parent component from the “App.jsx” file.

Now we have simply declared a “Children” component. Where the “Parent” component’s properties are inherited into the “Hello KingsCoder!“.

Let’s See The Output

To see the output of the above codes, start your dev server and browse the React App.

In my case, I Create my React App using Vite. So, I’ll type “npm run dev” in the terminal to start the dev server.

In the above screenshot, we can see “Hello KingsCoder!” gets “h1” and “green” color properties from its “Parent” component.

Types of React Component Composition

Above we saw an example of a basic React component composition. Sure, this is not the only method of composition. There are several other techniques for dealing with composition.

Some widely used component composition types are discussed below with examples.

Containment Composition:

One of the most popular methods of react component composition is containment. This is a most simple and widely used composition technique.

This method has a parent component that contains single or multiple properties (props) within itself. and has one or more child components. All the child components inherit the properties of the parent component.

import React from 'react';
    
// Creating a Parent Component
function Parent({ children }) {
  return <h1 style={{color:"Blue"}}>{children}</h1>;
}

// Creating a Child Component
function Child() {
  return <div>Hello KingsCoder! I'm a child component</div>;
}

function App() {
  return (
    <Parent>
      <Child />
    </Parent>
  );
}

export default App;

In the above code, the “Parent” component contains the “h1” and “color” properties of the text.

The “Child” component inherits the properties of its “Parent” component.

So in the output, you will see the “Hello KingsCoder! I’m a child component” as a heading. And the text color will be Blue.

Specific (Specialized) Composition:

A component that is created for a specific reason only to perform a specific task. Which inherits from a broad or generic component’s property. That is called specialization composition.

Sometimes a parent can have multiple properties. But sometimes the child component requires only a specific property from its parent. To do that, we can use the specialized composition technique.

import React from 'react';

// Creating a Parent Component
const ParentComponent = () => {
  const color1 = 'red';
  const color2 = 'green';
  const color3 = 'blue';

  return (
    <div>
      <h3>I'm a Parent. I have <span style={{color:color1}}>Red</span>, <span style={{color:color2}}>Green</span>, and <span style={{color:color3}}>Blue</span> color properties.</h3>
      <ChildComponent color={color2} />
    </div>
  );
};

// Creating a Child Component
const ChildComponent = ({ color }) => {
  return (
    <div>
      <p style={{ color }}>I'm a Child. I only inherited specifically "green" color property from my Parent.</p>
    </div>
  );
};

// Output in The Browser
const App = () => {
  return <ParentComponent />;
};

export default App;

Above “ParentComponent” carries three Color properties, Red, Green, and Blue. From these colors, “ChildComponent” has received only green color. So it is a specialized component composition.

For the sake of this example, we use colors as a property. But if you want, you can use the same technique with any kind of property instead of colors.

Context API Composition:

With the Context API, you can exchange values between multiple components without manually passing props.

This is a magical feature of React development. Which gives you the capability to handle many complex functionalities very easily. Context API helps you to build dynamic components in your application.

Suppose you want to create a component, when every time a user logs in, you want to show the user’s profile name. Sure, you can do this using other composition techniques if you want. But Context API will give you more dynamic flexibility in this task.

import React, { createContext, useContext } from 'react';

// Create a context with a default value
const ThemeContext = createContext('light');

// Child Component that consumes the context
const ThemedComponent = () => {
  const theme = useContext(ThemeContext);

  return (
    <div>
      <p>This component uses the theme: {theme}</p>
    </div>
  );
};

// Parent Component providing the context value
const ParentComponent = () => {
  return (
    <div>
      <ThemedComponent />
    </div>
  );
};

// Example usage with the Context Provider
const App = () => {
  return (
    <ThemeContext.Provider value="dark">
      <ParentComponent />
    </ThemeContext.Provider>
  );
};

export default App;

First of all, we declared “ThemeContext“. Using “createContext“, the default value is set to “light“.

Next, we create a child component that accepts the default value of the theme context.

Next, we create a parent component that returns the child component.

After that, here comes the most important part. Now we will change the value of “ThemeContext” with the help of Context Provider. It will dynamically assign the changed value first to the child and then to the parent component.

If you notice the code, you will see that the default value of “ThemeContext” was “light“. After that, we set the new value to “dark” through “ThemeContext.Provider“. It will change the output. And will show you the “dark” in the browser.

Render Props:

Render Props is a composition method that allows you to exchange data between two components by calling functions.

A child component calls data, behavior, or properties from the parent component through functions. The function then returns the React element as a callback. This is the workflow of the Render props method.

Render props composition technique is also quite popular and effective. Let’s see an example below.

import React from 'react';

// Creating a Parent Component with some data
const ParentComponent = ({ render }) => {
  const data = 'There are some data.';

  return (
    <div>
      {render(data)}
    </div>
  );
};

// Creating a Child Component to fetch data from Parent Component
const ChildComponent = () => {
  return (
    <div>
      <ParentComponent
        render={(data) => (
          <div>
            <h1>{data}</h1>
          </div>
        )}
      />
    </div>
  );
};

// Output the example
const App = () => {
  return <ChildComponent />;
};

export default App;

Notice above that “ChildComponent” is told to fetch data from “ParentComponent” using the “render()” function.

Then the “render()” function callback the “data” variable from the “ParentComonent” as a React element.

Finally the “ChildComponent” inherits the data contained in the “data” variable.

Higher Order Component (HOC) Composition:

Higher-order components are an important part of React development. It is very powerful and can be very complex depending on the type of project. But in this blog post, we will look at a simple (HOC) composition example.

Higher Order Components (HOC) will be discussed in more depth in another blog post later.

Before giving a sample code example, we need to know fundamentally what higher-order components actually are.

In simple terms, (HOC) in React is a function that takes an existing component as an argument and returns a new component. (HOC) adds or subtracts some extra parameters or behavior to the new component returned.

Basically, the functionality of the new components is increased or decreased according to the needs of the project. let’s see a simple example:

import React from 'react';
    
// Create a Higher-Order Parent Component
function ParentComponent(Component) {
  return function AdditionalProp(props) {
    return <Component {...props} additionalProp="It is a New Property." />;
  };
}

//Create a Child Component
function ChildComponent({ additionalProp }) {
  return <h1>{additionalProp}</h1>;
}

// Applying HOC to the New Child Component
const NewChild = ParentComponent(ChildComponent);

function App() {
  return <NewChild />;
}

export default App;

A (HOC) function named “ParentComponent” above takes “ChildComponent” as an argument. and returns the “NewChild” component.

Here the extra text property (props) is added to the “NewChild” component using (the HOC) technique. I repeat this is a very simple (HOC) example. This can be more complex depending on the needs of your application.

Composition with React Hooks:

React’s hooks are an amazing feature. In earlier legacy versions of React, you had to create class-based components to use “useState”, “useEffect” and other features. But since version 16.8 of React, you get the opportunity to use those features in functional components with the help of hooks.

This is a game changer in my view. Because class-based components would have increased the complexity of the project. Also, the parent and child of the class component are too tightly relational, which dramatically reduces the component’s flexibility. However, functional components do not have these problems.

So sharing data from one functional component to another through hooks opens up a lot of possibilities.

Let’s see a code sample below:

import React, { useState, useEffect } from 'react';

// Child Component using useState and useEffect hooks
const CounterComponent = () => {
  const [count, setCount] = useState(0);

  // useEffect hook to perform side effects (in this case, logging)
  useEffect(() => {
    console.log(`Count has been updated: ${count}`);
  }, [count]); // Dependency array ensures the effect runs only when count changes

  return (
    <div>
      <h2>Counter Component</h2>
      <p>Count: {count}</p>
      <button onClick={() => setCount(count + 1)}>Click Here</button>
    </div>
  );
};

// Parent Component
const ParentComponent = () => {
  return (
    <div>
      <h1>Parent Component</h1>
      <CounterComponent />
    </div>
  );
};

// Example Output
const App = () => {
  return (
    <div>
      <h1>App Component</h1>
      <ParentComponent />
    </div>
  );
};

export default App;

The above code shows a simple example of a React hook. Where the “Click Here” button will update “useState”. And every time “useState” is updated, as a side effect, the browser will show an update message in the console log.

And every time it is clicked, the counter update will show as output in the browser.

Compounding Composition:

In the compound composition approach, the parent component operates and manages all properties, states, and behaviors. And shares with the child components. On the other hand, the child component is responsible for rendering them.

Compound Composition As the name usually implies, it is organized by the compounding of multiple components. That is, here the parent and child components are working together.

import React, { useState } from 'react';

// Parent Compound Component
const Accordion = ({ children }) => {
  const [activeIndex, setActiveIndex] = useState(null);

  const handleItemClick = (index) => {
    setActiveIndex(index === activeIndex ? null : index);
  };

  // Pass state and handler function to child components
  const accordionItems = React.Children.map(children, (child, index) =>
    React.cloneElement(child, {
      isActive: index === activeIndex,
      onItemClick: () => handleItemClick(index),
    })
  );

  return <div>{accordionItems}</div>;
};

// Child Compound Component
const AccordionItem = ({ isActive, onItemClick, title, content }) => {
  return (
    <div>
      <div
        style={{
          cursor: 'pointer',
          borderBottom: isActive ? 'none' : '1px solid #ccc',
          padding: '8px',
          background: isActive ? '#f0f0f0' : 'white',
        }}
        onClick={onItemClick}
      >
        {title}
      </div>
      {isActive && <div style={{ padding: '8px' }}>{content}</div>}
    </div>
  );
};

// Example usage of Compound Components
const App = () => {
  return (
    <Accordion>
      <AccordionItem title="Section 1" content="Content for Section 1" />
    </Accordion>
  );
};

export default App;

At first “Accordion“, a parent component manages the “activeIndex” state.

Secondly, “AccordionItem” is a child component that receives its parent component’s state, data, and behavior through props.

And finally “AccordionItem” returns the parent’s data as output. and shows in the browser.

Flexibility with Slots Composition:

React Slots is a simple but powerful technique. With slots, you can define where a child component or its elements will be rendered in your app. Which helps you a lot in dynamic layout building.

import React from 'react';
    
// Create a Slot Component
function SlotComponent({ title, content }) {
  return (
    <div className="slot">
      <div className="title-slot">{title}</div>
      <div className="content-slot">{content}</div>
    </div>
  );
}

function App() {
  return (
    <SlotComponent
      title={<h2>I'm the title slot.</h2>}
      content={<p>i'm the content slot</p>}
    />
  );
}

export default App;

Here two props “title” and “content” are given in the slot component. We can insert other valid React elements with the help of these props wherever we want in our app. Which will provide more flexibility in our application.

React Component Composition Pros

Reusability:

The main fundamental of React component composition is its reusability feature. This means you can create a parent component and use it throughout your project. As a result, you don’t have to code components with the same functionality over and over again.

Reusability will also increase the efficiency of your React application. Because if you write the same component repeatedly, your application will consume much more memory. Once a component is created it will be reused everywhere so the app will consume less memory. And that’s a clear benefit.

It will also save you a lot of development time for reusability.

Flexibility:

You can create combinations or variations of multiple components using the composition technique. Which will make your React application more flexible.

You can create more advanced and modern apps through different compositions of Composition.

For example, to make a very tasty pizza, many combinations of ingredients have to be made. Similarly, creating the best user experience requires many graphics, interactions, and functionalities. And you can create all these combinations through React component composition.

Easy Navigation:

Let’s imagine you are working on an ambitious project. Of course, the project is a bit big and complex. And while working, you need to make a lot of components. But the problem is now it is quite difficult to navigate the huge numbers of components.

A simple solution to this problem can be found in the composition technique. Since this technique, reused the same parent component multiple times. So you will need to make fewer components. And parent-children tree structure will be maintained through composition.

As a result, you can easily navigate components no matter how big a project you are working on.

Maintainability and Scalability:

Using the React component composition technique, it is possible to maintain a coherent structure of the components of the entire project. As a result, it is very easy to maintain.

It provides the ability to maintain specific parts of the project. Which leads to fast and reliable maintainability.

And because composition allows parent-child components to be organized into groups. So they can easily be scaled up at any point in time as per the needs of the project.

Better Code Organization:

Component composition promotes following a clean and well-organized code structure. it encourages developers to organize the codebase according to UI hierarchically.

As a result, the whole project becomes well organized. Which then makes the project’s codebase easy to read.

Collaboration:

When you’re working on a serious and big React application, you’ll need to collaborate with other developers. Or you may work as a developer in a company. Where the whole developer team will have to work on the application.

In all these scenarios you should follow industry standards. And since the codebase will be well organized through component composition, collaboration with other developers will be easier.

Efficient Debugging:

When an issue or bug occurs in the app. Then if the component compositions are well organized with UI hierarchy. Then the issue can be easily identified. It helps to isolate the problematic part of the app. And it won’t affect the entire app.

As a result, you can quickly and efficiently debug app problems.

React Component Composition Cons

Extra-Rendering:

Using React component composition can sometimes require your app to do additional rendering. This is usually the case in large-scale applications. Sometimes too many components i.e. highly modular approach leads to over-rendering.

So use component composition smartly. Don’t overcomplicate it.

Testing:

Component composition requires more testing and mocks to create.

Because there is a possibility of more interacting scenarios in composition compared to just simple components. More scenario possibilities mean more tests are needed to cover the entire aspect.

Complexity:

Sometimes the complex hierarchy of composition is a bit difficult for beginners to understand.

Especially as your application grows, it is quite difficult to understand and maintain the app for beginners.

Let’s Wrap

First of all, thank you very much for taking the time to read the article.

So in this article, we have seen the basics of React component composition. You should definitely use component composition. In this article, you can also see the benefits of composition. So use it anyway in your codebase.

I have tried to explain as clearly as possible. Sample code is provided in each section. Try it yourself a few times and you will see that everything seems clear and easy.

If you have any more questions or have something to share on this topic, the comment box is definitely open for you.

Happy Coding!

Leave a Comment

Your email address will not be published. Required fields are marked *

Scroll to Top