Component Composition

Difficulty: Beginner

Component composition is the fundamental pattern in React for building complex UIs from simple, reusable pieces. Instead of creating one massive component, you break your UI into a tree of smaller components, each responsible for a specific piece. A parent component renders child components, and those children can render their own children, forming a component tree.

The `children` prop is a special prop in React that lets you pass elements between the opening and closing tags of a component. When you write `<Card><p>Hello</p></Card>`, the `<p>Hello</p>` is automatically available inside Card as `props.children`. This is incredibly powerful because it lets you create wrapper or layout components that do not need to know their content ahead of time. A Card component, for example, can handle styling while the parent decides what goes inside it.

Component composition follows a top-down data flow. The root component (usually App) sits at the top of the tree and renders other components, passing data down through props. Each component in the tree is independent and reusable - you can use the same Button component in a form, a dialog, or a navbar. This is different from template-based frameworks where you might inherit from a base template.

React strongly favors composition over inheritance. In object-oriented programming, you might create a SpecialButton that extends Button. In React, you achieve the same thing by composing: creating a SpecialButton that renders a Button with specific props. The React team has found that composition solves every use case that inheritance would, without the tight coupling that inheritance creates.

When composing components, think in terms of 'what does this component need to do?' and 'what pieces make up this UI?'. A good component tree has clear boundaries: each component does one thing well, accepts the data it needs via props, and can be understood in isolation. This makes your code easier to test, debug, and modify over time.

Code examples

Nesting Components

// Child components
function Header() {
  return { type: 'header', props: { children: 'My App' } };
}

function Content() {
  return { type: 'main', props: { children: 'Page content here' } };
}

function Footer() {
  return { type: 'footer', props: { children: '© 2026' } };
}

// Parent component composes children
function App() {
  return {
    type: 'div',
    props: {
      children: [Header(), Content(), Footer()]
    }
  };
}

const app = App();
app.props.children.forEach(child => {
  console.log(child.type + ': ' + child.props.children);
});

The App component composes Header, Content, and Footer together. Each child component is independent and reusable.

Using the Children Prop

// A wrapper component that uses children
function Card(props) {
  return {
    type: 'div',
    props: {
      className: 'card',
      children: props.children
    }
  };
}

// Using Card with different content
const profileCard = Card({ children: 'User Profile: Alice' });
const statsCard = Card({ children: 'Total Sales: 150' });

console.log(profileCard.props.className + ' -> ' + profileCard.props.children);
console.log(statsCard.props.className + ' -> ' + statsCard.props.children);

The Card component doesn't know what content it will wrap. The `children` prop lets the parent decide what goes inside, making Card highly reusable.

Building a Component Tree

function Button(props) {
  return { type: 'button', props: { label: props.label, variant: props.variant } };
}

function NavItem(props) {
  return { type: 'li', props: { children: props.text, active: props.active } };
}

function Navbar() {
  const items = [
    NavItem({ text: 'Home', active: true }),
    NavItem({ text: 'About', active: false }),
    NavItem({ text: 'Contact', active: false })
  ];
  return { type: 'nav', props: { children: items } };
}

const nav = Navbar();
nav.props.children.forEach(item => {
  const marker = item.props.active ? ' (active)' : '';
  console.log(item.props.children + marker);
});

The Navbar component builds a tree by composing NavItem components. Each NavItem is independent and receives its data through props.

Key points

Concepts covered

component nesting, children prop, component trees, composition vs inheritance, reusability