Technology · React

React Props and Children

Master prop drilling, children, and patterns for composable component APIs.

TL;DR
  1. 01Pass data to children using props in a parent component.
  2. 02Use children to accept JSX from parent components.
  3. 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.

React PortalsReact Ref Patterns