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.
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.
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.
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
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.
AI tutor
The optional AI tutor isn't enabled in this deployment. All lessons, exercises, quizzes, and search work fully without it.