[ReactJS]

4 Oct 2025

-

3 min read time

Advanced Guide on React Component Composition

Master React component composition with this comprehensive guide: learn “Thinking in React,” core patterns like children, render props, HOCs, and compound components, plus advanced techniques including performance tweaks, server components, dynamic slots, and custom hooks for scalable, maintainable apps.

Kalle Bertell

By Kalle Bertell

Advanced Guide on React Component Composition

Mastering React Component Composition

In React, component composition means building a UI from smaller components by nesting them or passing content and behavior through props, rather than relying on component inheritance. For example, a Modal can render different dialog content passed as its children while keeping the same reusable shell. This guide starts with “Thinking in React,” then covers children, render props, HOCs, and compound components before moving into performance, server components, dynamic slots, configuration trade-offs, and custom hooks.

Image

Thinking in React

React’s official guide breaks UI design into five steps. Following them helps you build predictable, maintainable layouts.

  1. Break UI into a component hierarchy

    Sketch your app and identify self-contained pieces.

  2. Build a static version in React

    Render hard-coded data to confirm your component tree.

  3. Identify the minimal (but complete) representation of UI state

    Find every changing value your UI needs.

  4. Identify where your state should live

    Lift state up to the closest common ancestor that needs it.

  5. Add inverse data flow

    Pass event handlers downward so children can update shared state.

Image

React’s official guide on Thinking in React provides a detailed walkthrough.

Core Composition Patterns

Component composition replaces inheritance with simple functions and props, making your UI more modular.

1. Children Prop

Pass nested JSX directly into a component.

  <Modal>
  <h2>Title</h2>
  <p>Content goes here.</p>
</Modal>

2. Render Props

Provide a function as a prop to share state or behavior.

  <DataFetcher render={data => <List items={data} />} />

3. Higher-Order Components (HOCs)

Wrap a component to inject additional props or logic.

  const withAuth = Wrapped => props => (
  isAuthenticated ? <Wrapped {...props} /> : <Redirect to="/login" />
);

Benefits

  • Reusability

  • Readability

  • Testability

Anti-patterns to avoid

  • Deep nesting of HOCs

  • Tight coupling between wrapper and wrapped components

Learn more in Robin Wieruch’s guide on React component composition .

Pattern

Description

Code Example

Pros

Cons

Children Prop

Pass JSX as children to a component

`<Modal><p>Content</p></Modal>`

Simple, flexible

Limited to layout composition

Render Props

Use a function prop to share state or behavior

`<DataFetcher render={data => <List items={data} />} />`

Dynamic, decouples state

Verbose, can cause prop drilling

Higher-Order Components

Wrap components to add props/logic

`withAuth(WrappedComponent)`

Code reuse, logic separation

Can lead to deep nesting

Compound Components

Compound components let you share implicit state without polluting global context.

  • Definition: A parent component manages state and syncs data across its children.

  • Implementation: Use `React.Children` and `React.cloneElement` to pass props.

  • Example:

      import React, { useState } from "react";
    
    function Tab({ label, isActive, onSelect, children }) {
      return (
        <div>
          <button type="button" onClick={onSelect} aria-expanded={isActive}>
            {label}
          </button>
          {isActive && <div>{children}</div>}
        </div>
      );
    }
    
    function Tabs({ children }) {
      const [active, setActive] = useState(0);
    
      return (
        <div>
          {React.Children.map(children, (child, i) =>
            React.cloneElement(child, {
              isActive: i === active,
              onSelect: () => setActive(i)
            })
          )}
        </div>
      );
    }
    
    // Usage: direct Tab children receive the props supplied by Tabs.
    // <Tabs>
    //   <Tab label="Overview">Overview content</Tab>
    //   <Tab label="Details">Details content</Tab>
    // </Tabs>
    
  • Use cases: Tab lists, Accordions, Toggle groups

  • Limitations: Not ideal if children aren’t direct descendants

Detailed walkthrough at Patterns.dev on compound components .

Advanced Composition Techniques

As your app scales, you’ll face new challenges. These advanced topics will give you extra tools.

Performance Implications of Deep Component Trees

Every extra layer adds work during reconciliation. To mitigate:

Image

Server Components and Composition

React Server Components let you split rendering between server and client. You can mix them in one tree, but you must consider:

  • State can’t live in server components (no hooks that use browser APIs)

  • Data fetching happens on the server, reducing bundle size

  • For interactivity, a Server Component can pass server-rendered content as children to a Client Component; the Client Component should not directly import the Server Component

Read the Server Components RFC on GitHub .

Dynamic Slot-Based Composition Patterns

Inspired by Web Components’ `<slot>`, you can emulate dynamic slots in React:

  1. Define named areas via props

  2. Use context to register slot content

  3. Render slots by matching names

This approach shines in design systems where consumers inject arbitrary pieces:

  <Card>
  <Card.Slot name="header"><h1>Title</h1></Card.Slot>
  <Card.Slot name="body"><p>Details…</p></Card.Slot>
  <Card.Slot name="footer"><button>Save</button></Card.Slot>
</Card>

Learn how to build this in a Dev.to post on component slots in React .

Composition vs. Configuration: Choosing Your Approach

Should you compose via React elements or configure with props/objects?

Approach

Pros

Cons

Component Tree

Highly flexible, type-safe in TS

More boilerplate, nested

Props/Objects

Simple data structures, less nesting

Less flexible, harder to type dynamic behavior

“When you need arbitrary JSX, compose. For data-driven UIs, configuration often wins.”

— Kent C. Dodds

Cross-Cutting Concerns with Custom Hooks + Composition

Custom hooks excel at extracting shared logic. In large apps you might need logging, analytics or feature flags without altering your JSX trees.

  function useAnalytics() {
  const track = event => console.log("Tracked:", event);
  useEffect(() => track("Page Viewed"), []);
  return { track };
}


// In your component
function Button(props) {
  const { track } = useAnalytics();
  return <button onClick={() => {
            track("Clicked"); 
            props.onClick(); 
         }}>
           {props.children}
         </button>;
}

You can find a tutorial on writing custom React hooks .

Putting It All Together

By combining “Thinking in React” with core patterns and these advanced strategies, you’ll build apps that are both maintainable and high-performing. Start with a clear component hierarchy, choose the right composition pattern, and layer in optimizations—whether through server components, dynamic slots, or custom hooks. Your React projects will handle complexity with confidence.

For help improving the structure of an existing application, explore our ReactJS & Next.js development and code audit services.

Kalle Bertell

By Kalle Bertell

More from our Blog

Keep reading