Introduction
In the last article, we learned how to pass data using props, passing values to a child component through attributes. But what if you want to pass not just data, but actual content like elements, text, or even other components between a component's opening and closing tags?
That's where children come in.
The children prop is a special built-in feature in React that allows components to receive and render whatever is nested inside them. It opens the door to more flexible and reusable UI patterns for building layout wrappers, modals, cards, and more.
In this article, we'll dive into how children work, how to access and use them effectively, and when they can be more powerful than regular props.
What are props.children?
Simply put, children refer to the content you put between a component’s opening and closing tags.
<!-- Syntax -->
<MyWrapper>
<p>This is a child element!</p>
</MyWrapper>
Here, <p>This is a child element!</p> is what we call props.children inside the MyWrapper component.
How to Use Children?
Parent component (App.js)
import './App.css';
import MyWrapper from './components/Wrapper';
function App() {
return (
<div>
<h1>This is parent component</h1>
<MyWrapper>
<p>This is wrapped content sent from parent component.</p>
</MyWrapper>
</div>
);
}
export default App;
Child component (Wrapper.js)
// Child component
const MyWrapper = (props) => {
return (
<div>
{props.children}
</div>
);
};
export default MyWrapper;
And this is how it looks.
![Parent component]()
Different Ways to Use Children
1. Basic JSX children(this one we saw above)
// Child Component
const Card = (props) => {
return (
<div>
<h1>Child: Card</h1>
{props.children}
</div>
);
};
// Parent Component
function App() {
return (
<div>
<h1>This is parent component</h1>
<Card>
<h2>Alex</h2>
<p>Age: 27</p>
<p>Profession: Engineer</p>
</Card>
</div>
);
}
![Card]()
Or you can also use Destructuring,
Note. In React, children is a special prop, i.e. reserved property in the React ecosystem.
const Card = ({ children }) => {
return (
<div>
<h1>Child: Card</h1>
{children}
</div>
);
};
2. Render with a fallback
const Alert = ({ children, type }) => {
const typeStyles = {
success: {
border: "1px solid #28a745",
backgroundColor: "#d4edda",
color: "#155724",
},
warning: {
border: "1px solid #ffc107",
backgroundColor: "#fff3cd",
color: "#856404",
},
error: {
border: "1px solid #dc3545",
backgroundColor: "#f8d7da",
color: "#721c24",
},
info: {
border: "1px solid #17a2b8",
backgroundColor: "#d1ecf1",
color: "#0c5460",
},
default: {
border: "1px solid #ccc",
backgroundColor: "#fff",
color: "#000",
},
};
const style = {
...typeStyles[type] || typeStyles.default,
};
return (
<div style={style}>
{children ? children : "Default alert message."}
</div>
);
};
export default Alert;
I am using a fallback in two places.
Content fallback
This ensures that if no content (children) is passed to the <Alert> component, it still shows a meaningful default message. This will happen in the case of info.
{children ? children : "Default alert message."}
Fallback for type
(typeStyles[type] || typeStyles.default)
Here, we're dynamically applying styles based on the type prop. If type is not provided, or it's not one of the predefined ones (success, error, etc.), it falls back to the "default" style.
This is how I am calling the Alert-component.
<Alert type="success">
Your form was submitted successfully!
</Alert>
<Alert type="warning">
Please check your inputs!
</Alert>
<Alert type="error">
Something went wrong.
</Alert>
<Alert type="info" />
![Component]()
As you can see, type=info takes the default style and the default message.
3. Use the first or last child only
const Wrapper = ({ children, position = "first" }) => {
const childrenArray = React.Children.toArray(children);
const content = position === "last"
? childrenArray[childrenArray.length - 1]
: childrenArray[0];
return (
<div>
<h3>{position === "last" ? "Last Child:" : "First Child:"}</h3>
{content}
</div>
);
};
export default Wrapper;
<Wrapper position="first">
<p>First child</p>
<p>Second child</p>
<p>Third child</p>
</Wrapper>
<Wrapper position="last">
<p>First child</p>
<p>Second child</p>
<p>Third child</p>
</Wrapper>
Last but not least:cloneElement
Why Use cloneElement?
- To inject props (like style, className, onClick, etc.) into children.
- To apply shared behavior or styling to any nested components.
- To avoid modifying the original child components directly.
import React, { cloneElement } from "react";
const Message = ({ text, color }) => {
return <p style={{ color }}>{text}</p>;
};
const ColorWrapper = ({ children }) => {
return cloneElement(children, { color: "green" });
};
function App() {
return (
<div>
<ColorWrapper>
<Message text="This is a green message!" />
</ColorWrapper>
</div>
);
}
export default App;
What’s Happening?
- <Message /> expects a text and color.
- Instead of giving color directly, we let ColorWrapper inject it.
- cloneElement(children, { color: "green" }) adds the color prop to <Message />.
Children vs Named Props
Use Case |
children |
Named Props |
JSX, HTML, components |
Best Fit |
Less Flexible |
Strings, small values |
Use Sparingly |
Best Fit |
Mistakes to Avoid
Mistake |
Problem |
Not rendering children |
The component shows nothing |
Assuming it's always an array |
Could be a single element or a string |
Modifying children directly |
Breaks immutability — use cloneElement |
Forgetting wrapper elements |
JSX must return a single root |
Conclusion
Children make your components more flexible. It's perfect for layouts, wrappers, and any situation where you want to plug in different JSX without changing the component itself.
Once you get the hang of it, you’ll find yourself using children all the time to build clean, reusable UIs.