beginner18 min

Props: Passing Data Down, One Way

Props are how a parent configures a child — read-only, one direction, always. Understanding why that constraint exists prevents an entire category of confusing bugs.

What you'll learn

  • Explain why props flow in one direction, parent to child
  • Identify a prop-mutation bug and explain why it breaks React's assumptions
  • Design a default-value strategy for optional props

Prerequisites

Explanation

A component is, at its core, a function. Props are its arguments. <CourseCard title="TypeScript" progress={40} /> is really just calling a function with an object: CourseCard({ title: "TypeScript", progress: 40 }). That framing explains the two rules that confuse beginners most.

Props are read-only. A JavaScript function that reassigns its own parameter doesn't change what the caller passed in — the caller's original value is untouched. React leans on this same guarantee deliberately: a child component must never mutate the props object it receives. If CourseCard could reach into its progress prop and change it, the parent's own state — the actual source of truth — would silently disagree with what's on screen, and there would be no way to reason locally about where a value actually comes from.

Data flows one way: parent to child. A parent can pass data down as props. A child cannot reach up and hand data back the same way — there is no equivalent of a prop flowing upward. When a child genuinely needs to communicate something to its parent (a button click, a form submission), the parent passes a function down as a prop, and the child calls that function. The data still only flows one direction (a function reference, passed down); it's the invocation of that function, not a return value flowing back up through props, that lets a child trigger a parent's behavior. This pattern — "lifting state up" and passing callbacks down — is covered in depth once state is introduced in the next lesson.

Default values matter for optional props. A Badge component that expects a tone prop but sometimes doesn't receive one needs an explicit fallback (tone = "neutral") rather than silently rendering undefined-driven styling. Deciding sensible defaults up front is part of designing a component's actual public interface — the same care you'd put into designing any function's parameters.

Example

Modeling a component as a plain function taking a props object — exactly what <CourseCard title="..." progress={40} /> becomes underneath JSX.

function CourseCard(props) {
  const tone = props.tone || "neutral"; // default for an optional prop
  return { title: props.title, progress: props.progress, tone };
}

console.log(CourseCard({ title: "TypeScript", progress: 40 }));
console.log(CourseCard({ title: "React", progress: 10, tone: "warning" }));

Try it yourself

Add a default value for a new optional 'showBadge' prop (default it to true), then use it below.

Code editor. Press Escape then Tab to leave the editor if keyboard focus becomes trapped. Press Control+Shift+M inside the editor to toggle Tab-key focus trapping.

Loading editor…

Guided exercise

Guided exercise

Here is a buggy component function that mutates its props object directly. Fix it (without mutating props) by writing a corrected version as fixedClampProgress, which returns a NEW object with progress clamped between 0 and 100, leaving the input props object untouched.

Checks: returns a correctly clamped value · does not mutate the original props object

Code editor. Press Escape then Tab to leave the editor if keyboard focus becomes trapped. Press Control+Shift+M inside the editor to toggle Tab-key focus trapping.

Loading editor…

Stuck? Get a hint.

Independent exercise

Independent exercise

Write a function withDefaults(props, defaults) that returns a new object where any key present in `defaults` but missing (or undefined) in `props` is filled in from `defaults`, without mutating either input object. Keys already present and defined in `props` should win.

Checks: preserves existing props · fills in missing props from defaults · an explicit prop wins over its default

Code editor. Press Escape then Tab to leave the editor if keyboard focus becomes trapped. Press Control+Shift+M inside the editor to toggle Tab-key focus trapping.

Loading editor…

Stuck? Get a hint.

Common mistakes

  • Mutating a props object directly inside a component instead of deriving a new value.
  • Trying to have a child component change a value and have that change magically reflect in the parent, without passing a callback function down for the child to call.
  • Forgetting sensible defaults for optional props, so a missing prop silently produces `undefined`-driven behavior instead of a clear fallback.

Knowledge check

Knowledge check

1. Why should a component never mutate the props object it receives?
2. How does a child component communicate something back up to its parent?
3. Why does a component like Badge benefit from an explicit default value for an optional `tone` prop?

Takeaway

Props are read-only, one-directional function arguments — a child changes its parent's behavior only by calling a callback the parent passed down, never by mutating props directly.

Summary

This lesson covered why props are read-only and flow one direction, how to fix a prop-mutation bug, and how to design sensible defaults for optional props.

References

Your notes

Notes save automatically.

Finished this lesson?

Mark it complete to track your progress and schedule a future review.