Go to Articles
September 11, 20235 min read

Optimizing React.js: Using Children Instead of Props

Imagine you're building a dynamic website with React.js, and you've just completed a feature-rich Paragraph component. This component accepts text, icons, videos, and even audio as props. It's a versatile tool ready to tackle various content challenges.

Now, fast forward a few months. Your project has grown, and new requirements emerge. Your manager asks for paragraphs with additional features like integrated maps, interactive charts, and social media widgets. You face a dilemma: should you keep modifying your trusted component of paragraphs, potentially introducing errors and complicating its code? Or is there a more elegant solution that ensures your component remains solid and easy to maintain?

This is where the power of using the children property of React.js takes relevance instead of the traditional props. In this article, we'll explore how adopting this approach can save you from the complexities of endless component modifications, promote clean and maintainable code, and align with software development principles like the Open/Closed Principle and the Single Responsibility Principle (SOLID principles).

Let's delve deeper into the world of optimizing React.js by leveraging children instead of props.

Use Cases for children in React.js

Before we start, I'd like to use a simple Paragraph component as an example.

Imagine this component receives its content through a prop called text, which takes text strings (and nothing else).

// With props
export const Paragraph = ({ text }) => {
    return (
        <p>{text}</p>
    )
}

Now, imagine some time has passed. You need a new element for your page: paragraphs with icons. You need them to have the same features and styles as the Paragraph component but with an icon on the left.

You try to implement the new functionality like this:

// With props
export const Paragraph = ({ text, renderIcon }) => {
    return (
        <p>
            {renderIcon && renderIcon()}
            {text}
        </p>
    )
}

Later, you're asked for two more new features: the ability to have an embedded audio and/or video file within the paragraph.

This is roughly how the component looks now:

// With props
export const Paragraph = ({ text, renderIcon, videoSource, audioSource }) => {
    return (
        <p>
            {renderIcon && renderIcon()}
            {text}
            {videoSource && (
                <video src={videoSource} />
            )}
            {audioSource && (
                <video src={audioSource} />
            )}
        </p>
    )
}

Now, you're asked for even more new features... well, that's enough! How much can the Paragraph component keep growing? We're modifying the component from within every time we need a new feature, which can lead to changes and unexpected errors in parts of the application we didn't anticipate.

Furthermore, the component starts to take care of many things that aren't its responsibility. It shouldn't be responsible for aspects unrelated to the paragraph's structure.

A better implementation would be to delegate that work to whoever is calling the Paragraph component, and have Paragraph internally only handle the content structure.

// With children
export const Paragraph = ({ children }) => {
    return (
        <p className='paragraph-structure'>{children}</p>
    )
}
// With children
import { Paragraph } from './paragraph'
import { VideoPlayer} from './video-player'
import CheckIcon from 'icon-library'

export const App = () => {
    return (
        <h1>Hello World</h1>
        <Paragraph>
            <CheckIcon >
            <span>This is my cool paragraph.</span>
            <a href='/'>This is my link inside my paragraph</a>
            <VideoPlayer />
        </Paragraph>

        <Paragraph>
            <CheckIcon >
            <span>This is my paragraph with fewer features.</span>
        </Paragraph>
    )
}

This way, our code:

  1. Adheres to the Open/Closed Principle, which is described as: "Software entities (classes, modules, functions, etc.) should be open for extension but closed for modification," or in other words: "your code should not require internal modification to extend its functionalities."
  2. Adheres to the Single Responsibility Principle, which is described as: "A module should have only one reason to change," or in other words, "a software module should focus on and be responsible for a single task."
  3. Saves us headaches when our software needs to grow, scale, and be maintained.

Should I Always Use children Instead of props?

Each implementation has its use cases and opportune moments to be used. props in React.js components are and will continue to be widely used to make a component reusable.

In general terms, using children in a component would be more convenient for:

  • A generic container component.
  • Layout components.
  • The content to render within a component.

On the other hand, props are better for:

  • Customization (styles, behavior).
  • Necessary information (list, object, or primitive).
  • Callback functions.
  • State management.

Throughout the article, we used a Paragraph component as a reference, but it's a very simple example. These same concepts can be applied to much more complex components and even to classes and functions in any programming language.

Conclusions

Instead of constantly modifying the component from within, we adopted the children approach. This change allowed us to keep our original component clean and focused on its main task: structuring content. Those who use the component are responsible for adding additional features.

This approach not only aligns with the Open/Closed Principle and the Single Responsibility Principle in software development but also provides us with more robust and maintainable code as our project grows and evolves.

In summary, by embracing React.js's philosophy of passing content as children, we are paving the way for agile and seamless development. Let's leverage this powerful tool to create robust and future-ready components in our React.js applications.

Made with 💙 by Diego Reyes