Technology · React
React Props and Children
Master prop drilling, children, and patterns for composable component APIs.
TL;DR
- 01Pass data to children using props in a parent component.
- 02Use children to accept JSX from parent components.
- 03Avoid prop drilling by using Context or composition patterns.
Basic Props
- Pass data to child components using props as an object.
function Greeting({ name, age }) { return <p>Hello {name}, age {age}</p>; } <Greeting name="Alice" age={30} /> - Destructure props for cleaner code in function components.
function Button({ label, onClick, disabled = false }) { return ( <button onClick={onClick} disabled={disabled}> {label} </button> ); } - Use default values for optional props.
function Card({ title = "Untitled", content }) { return <div><h2>{title}</h2><p>{content}</p></div>; } - Pass functions as props to handle events from children.
<Button onClick={() => handleClick()} label="Click me" />
Using Children
- Accept JSX as children to create flexible wrapper components.
function Card({ children, title }) { return ( <div className="card"> <h2>{title}</h2> <div className="content">{children}</div> </div> ); } <Card title="Welcome"> <p>This is card content</p> </Card> - Children is a special prop that contains nested JSX.
- Multiple children are passed automatically as an array.
<Container> <Header /> <Main /> <Footer /> </Container> - Use React.Children.map to iterate over children.
function Row({ children }) { return ( <tr> {React.Children.map(children, (child, index) => ( <td key={index}>{child}</td> ))} </tr> ); }
Prop Drilling and Solutions
- Prop drilling occurs when passing props through many levels.
// Level 1 <App user={user}> // Level 2 <Layout user={user}> // Level 3 <Sidebar user={user}> // Level 4 <Profile user={user} /> </Sidebar> </Layout> </App> - Use Context to avoid prop drilling for shared data.
const UserContext = createContext(); <UserContext.Provider value={user}> <App /> </UserContext.Provider> function Profile() { const user = useContext(UserContext); return <div>{user.name}</div>; } - Use composition to pass components instead of data.
<Layout sidebar={<Sidebar />}> <Main /> </Layout>
Component Composition Patterns
- Build flexible layouts by accepting components as props.
function Page({ header: Header, content: Content, footer: Footer }) { return ( <> <Header /> <Content /> <Footer /> </> ); } <Page header={<Header />} content={<MainContent />} footer={<Footer />} /> - Use the render prop pattern for dynamic content.
function DataFetcher({ render }) { const [data, setData] = useState(null); useEffect(() => { fetchData().then(setData); }, []); return render(data); } <DataFetcher render={(data) => <List items={data} />} /> - Compose small, focused components for reusability.
<Modal> <Modal.Header>Title</Modal.Header> <Modal.Body>Content</Modal.Body> <Modal.Footer>Actions</Modal.Footer> </Modal>
Advanced Patterns
- Clone and modify children with cloneElement.
function FormFields({ children, error }) { return React.Children.map(children, (child) => React.cloneElement(child, { error }) ); } - Use children as a function for render props.
function Toggle({ children }) { const [open, setOpen] = useState(false); return children({ open, toggle: () => setOpen(!open) }); } <Toggle> {({ open, toggle }) => ( <button onClick={toggle}> {open ? "Close" : "Open"} </button> )} </Toggle> - Spread remaining props to avoid manual forwarding.
function Button({ label, ...rest }) { return <button {...rest}>{label}</button>; }
Tip: Use composition and Context instead of drilling props through many levels — it makes code cleaner and easier to maintain.
Warning: Don't pass too many props to a single component — it's a sign to break it into smaller, more focused components.