Chapter 05 of 14 · browse all
Chapter 05

Props

How data flows from a parent component into a child, and why it only flows that way.

Passing data in

Props are the arguments of a component. You write them like HTML attributes, and React collects them into a single object passed as the first parameter.

function Greeting(props) {
  return <h1>Hello, {props.name}!</h1>;
}

function App() {
  return <Greeting name="Ada" />;
}

Values other than strings go in braces:

<Product name="Keyboard" price={49.99} inStock={true} tags={["input", "usb"]} />

A prop with no value is shorthand for true, so inStock and inStock={true} mean the same thing.

Destructuring in the signature

Reading props.something repeatedly gets noisy. Most code destructures the props object directly in the parameter list.

function Product({ name, price, inStock }) {
  return (
    <div>
      <h3>{name}</h3>
      <p>{price}</p>
      {inStock ? <span>In stock</span> : <span>Sold out</span>}
    </div>
  );
}

Default values

function Button({ label = "Save changes", kind = "primary" }) {
  return <button className={kind}>{label}</button>;
}

The default applies when the prop is missing or explicitly undefined. Passing null does not trigger it.

The children prop

Whatever you put between a component's opening and closing tags arrives as a prop named children. This is how wrapper components are built.

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

function App() {
  return (
    <Panel title="Notes">
      <p>Anything can go here.</p>
      <button>Add note</button>
    </Panel>
  );
}

Props are read-only

A component must never change its own props. They belong to the parent.

function Total({ amount }) {
  amount = amount * 1.2;   // Wrong: mutating a prop
  return <p>{amount}</p>;
}

function Total({ amount }) {
  const withVat = amount * 1.2;   // Right: derive a new value
  return <p>{withVat}</p>;
}

The same applies to objects and arrays passed as props. Copy them before changing them rather than editing in place.

Data flows down

React's data flow is one-directional: a parent passes props to a child, never the reverse. If a child needs to affect the parent, the parent passes down a function for the child to call.

function SearchBox({ onSearch }) {
  return <button onClick={() => onSearch("react")}>Search</button>;
}

function App() {
  function handleSearch(term) {
    console.log("Searching for", term);
  }
  return <SearchBox onSearch={handleSearch} />;
}

The child does not know what happens next, and does not need to. That separation is what makes components reusable in a second place.

Spreading props

Spread syntax forwards a whole object of props at once. It is convenient for wrapper components and easy to overuse.

const settings = { name: "Keyboard", price: 49.99 };

<Product {...settings} />

Spreading everything makes it hard to see what a component actually receives. Prefer naming props explicitly unless you are deliberately forwarding an unknown set.