Skip to main content
Back to module
React Fundamentals · Components and Props

Props and composition

Reading39mLesson 3 of 3 in this module

Pass data into components with props, wrap content with children, and compose small components into screens.

Why it matters

Components become useful when you can reuse them with different data. Props are how a parent passes data into a child, and composition is how you build big screens out of small, focused components instead of one huge file. Good prop design is what makes a component library such as a design system pleasant to use.

Props are function arguments

A component is a function. Its first argument is an object holding every attribute you wrote on the JSX tag:

function Greeting(props) {
  return <p>Hello, {props.name}! You have {props.count} messages.</p>;
}

<Greeting name="Ada" count={3} />

Strings can be written in quotes; anything else (numbers, booleans, arrays, objects, functions) goes in braces. Most code destructures the props object in the parameter list, often with default values:

function Greeting({ name, count = 0 }) {
  return <p>Hello, {name}! You have {count} messages.</p>;
}

A prop written with no value, such as <Button disabled />, is true.

Props are read-only

A component must never change its props. props.count = 5 is a bug: the data belongs to the parent. If something needs to change over time, it becomes state (next module), usually in the parent, which then passes new props down. Data flows one way: from parent to child.

React components should also be pure with respect to their props: given the same props, return the same output, with no side effects during rendering. That predictability is what lets React re-render safely whenever it needs to.

Passing functions as props

Children communicate back up by calling functions the parent passed in:

function LikeButton({ liked, onToggle }) {
  return <button onClick={onToggle}>{liked ? "Unlike" : "Like"}</button>;
}

// In the parent:
<LikeButton liked={isLiked} onToggle={() => setIsLiked(!isLiked)} />

By convention, event-handler props are named onSomething, and the functions that handle them in the parent are named handleSomething.

Children and composition

Whatever you put between a component's opening and closing tags arrives as the special children prop:

function Card({ title, children }) {
  return (
    <section className="card">
      <h2>{title}</h2>
      <div className="card-body">{children}</div>
    </section>
  );
}

<Card title="Ada Lovelace">
  <p>Wrote the first published computer program.</p>
  <Badge label="Pioneer" />
</Card>

Card does not need to know what is inside it. It provides the frame (border, heading, spacing), and the caller decides the content. This is composition: combining general-purpose containers with specific content. It is React's main tool for reuse, preferred over inheritance.

You can also pass elements in named props when a component has more than one "slot":

<Card title="Profile" footer={<a href="/edit">Edit profile</a>}>
  ...
</Card>

Optional pieces

Render optional parts only when a prop is present:

function Card({ title, children, footer }) {
  return (
    <section className="card">
      <h2>{title}</h2>
      <div className="card-body">{children}</div>
      {footer && <footer>{footer}</footer>}
    </section>
  );
}

Building class names from props

Variants such as size or colour are usually props that choose a CSS class:

function Badge({ label, tone = "info" }) {
  return <span className={"badge badge-" + tone}>{label}</span>;
}

<Badge label="Overdue" tone="warning" />

Keep the number of variant props small and their allowed values clear. A component with fifteen boolean props is a sign it should be split.

Prop drilling

When data must pass through several components that do not use it, just to reach a deep child, that is prop drilling. A little is normal and easy to follow. When it gets painful, composition (passing children so the deep child is created higher up) or context (module 3) can help.

Common mistakes

  • Changing a prop inside the child instead of asking the parent to change it.
  • Writing count="3" (a string) when you mean the number count={3}.
  • Calling the handler during render: onClick={onToggle()} runs immediately. Pass the function: onClick={onToggle}.
  • Spreading every prop onto a DOM element (<div {...props}>) and leaking unknown attributes onto the page.

Recap

  • Props are the object of attributes passed to a component; destructure them and give defaults.
  • Props are read-only and flow from parent to child; children talk back by calling function props.
  • children lets a component wrap any content; composition beats huge, all-knowing components.
  • Use props to choose variants, and render optional parts only when their prop exists.

Try it

Open the Do tab to build a Card with a title, children and an optional footer, plus a Badge with a default tone, then compose them.

Also in this lesson: