Why React Components Must Be Pure

Props are immutable because mutating them is a side effect. React builds on pure functions because purity is what makes the component model predictable and safe to optimize.

June 18, 20264 min read3 / 3

I knew props were read-only before I understood why.

"Don't mutate props" felt like a rule handed down from framework designers who wanted to make things complicated. Then I looked at what would actually happen if you did mutate them.

The rule became obvious.

Props Is Just an Object

When React calls your component, it passes a props object as the argument. That object contains all the values the parent passed down.

TSX
function Pizza({ name, price }: { name: string; price: number }) { // props is: { name: "Margherita", price: 10 } }

Now, what if you changed price directly inside the component?

TSX
function Pizza({ name, price }: { name: string; price: number }) { price = price + 5; // don't do this }

In isolation this looks harmless. But props is a reference to an object that lives in the parent. When you modify it, you are modifying the parent's data.

That is a side effect. You changed something outside the scope of your own function.

What a Side Effect Is

A pure function has one rule: given the same inputs, it always returns the same output, and it changes nothing outside itself.

A side effect is anything that breaks that rule: reading or writing to something outside the function scope. Network requests, timers, DOM mutations, and mutating a prop object the parent handed you all qualify.

React requires components to be pure with respect to props and state. The component can use them, read them, derive values from them. It cannot reach outside itself and change them.

TSX
// Pure: reads props, returns JSX, touches nothing else function Badge({ label }: { label: string }) { return <span className="badge">{label}</span>; } // Impure: modifies the object it received function Badge({ style }: { style: React.CSSProperties }) { style.color = 'red'; // mutates caller's object return <span style={style}>badge</span>; }

The second component is broken. The parent passed a style object expecting it to come back unchanged. Any other component using the same object now has a surprise red color.

Why React Needs Purity

Purity is not just a rule for your own sanity. It is what makes React's optimization model work.

React can re-render a component at any time: on every state change, on every parent render. If components had side effects on their inputs, rendering would be unpredictable.

The result of a render would depend on how many times it had been called, what it had changed on previous calls, and what order the renders happened in.

Purity means every render is self-contained. React can throw away renders, run them twice (it does this in Strict Mode), or skip them entirely, and the app still works correctly because no single render leaves behind damage.

One-Way Data Flow Is the Same Principle

React enforces that data only flows from parent to child. A child component cannot push data up to its parent directly.

This is not a limitation. It is purity at the system level.

If any component could reach up and modify a parent's state, you would have the same problem as DOM-as-state-storage: multiple things writing to the same data, in no predictable order, with no single owner.

One-way data flow means every piece of state has exactly one owner. That owner decides when and how it changes. Everything downstream just reads.

Plain text
App (owns data) down props Header (reads) down props Nav (reads)

Data flows down. Events flow up, but through callbacks the parent explicitly passed down as props, not through direct mutation. The parent stays in control.

State Is the Escape Valve

Props being immutable raises an obvious question: how does anything ever change?

The answer is state. State is data the component owns and can update. When state updates, React re-renders the component with the new value.

The difference from props:

PropsState
Owned byParentThe component itself
Can be changed byParent onlyThe component itself
What triggers re-renderParent re-renderssetState call

When a child needs to trigger a change in the parent, the parent passes down a callback function as a prop. The child calls it. The parent updates its own state. React re-renders.

This is lifting state up. It is how React keeps one-way flow intact even when data needs to travel upward.

The Rule in One Sentence

A component is a pure function of its props and state: given the same inputs, it always renders the same output, and it never modifies anything outside itself.

Everything in React is built on the assumption that this rule holds: the reconciler, Strict Mode's double-mount, server components, concurrent rendering.

The Essentials

  1. Props are immutable because they are a reference to the parent's data. Mutating props is a side effect: you change something outside your function scope, which corrupts the parent's data unexpectedly.
  2. React requires pure components because purity makes renders safe to repeat, reorder, or skip. An impure component with side effects on its inputs produces unpredictable results when React re-renders.
  3. One-way data flow is purity at the architecture level. Every piece of state has one owner. Data flows down as props. Changes flow up through callbacks. Nothing writes to another component's state directly.

Further Reading and Watching