Volume I index
Components Before Abstractions

Props as Inputs

Ishtmeet Singh @ishtms/July 14, 2026/8 min read
#react#props#components#jsx#data-flow

Two LessonCard elements can use the same component function and display different lessons. Props carry the values supplied by each element into its component call.

jsx
<LessonCard title="Props as Inputs" minutes={14} />
<LessonCard title="Children as Composition" minutes={12} />

React creates two element descriptions with the same component type and different props. During rendering, React calls LessonCard for each occurrence and passes one props object.

text
first element  -> { title, minutes }
second element -> { title, minutes }

The function code is shared. Each rendered occurrence receives its own input object.

Receive One Object

Start without destructuring.

jsx
function LessonCard(props) {
  return (
    <article>
      <h2>{props.title}</h2>
      <p>{props.minutes} min</p>
    </article>
  );
}

React calls the function with one argument. That argument is the props object for the current element.

This signature does not receive one positional argument per JSX attribute.

jsx
function LessonCard(title, minutes) {
  // wrong model
}

title would receive the whole props object. minutes would not receive the second JSX prop.

The object can be inspected temporarily.

jsx
function LessonCard(props) {
  console.log(props);
  return <h2>{props.title}</h2>;
}

Development Strict Mode can call the component again, so the log can appear more than once. Remove the log after observing the object.

The Parent Supplies the Props

LessonList creates the child element.

jsx
function LessonList() {
  return (
    <LessonCard
      title="Props as Inputs"
      minutes={14}
    />
  );
}

For this element, LessonList is the parent component and LessonCard is the child component. The parent chooses the values because it creates the element.

The child reads those values while calculating its output.

jsx
function LessonCard(props) {
  return <h2>{props.title}</h2>;
}

This direction is often described as data flowing down the component tree. A parent passes props to a child. If the child needs to report an event upward, the parent can supply a callback prop.

Quoted Props and Braced Props

A quoted JSX prop value is a string.

jsx
<LessonCard title="Props as Inputs" />

Braces evaluate a JavaScript expression.

jsx
<LessonCard minutes={14} />

Without braces, the number would be text.

jsx
<LessonCard minutes="14" />

Both can display 14, but their JavaScript types differ. Arithmetic, sorting, and TypeScript contracts depend on the correct type.

Pass a variable through braces.

jsx
const lessonMinutes = 14;
<LessonCard minutes={lessonMinutes} />

Pass an object the same way.

jsx
const lesson = {
  title: "Props as Inputs",
  minutes: 14,
};

<LessonCard lesson={lesson} />

Props can carry strings, numbers, booleans, objects, arrays, functions, React nodes, and other JavaScript values supported by the current environment. What the child does with a value determines whether it can be rendered, called, or passed onward.

Destructure Named Properties

Parameter destructuring creates local bindings from the props object.

jsx
function LessonCard({ title, minutes }) {
  return (
    <article>
      <h2>{title}</h2>
      <p>{minutes} min</p>
    </article>
  );
}

React still passes one object. JavaScript applies the destructuring pattern before the function body uses the bindings.

The property names must match the JSX prop names.

jsx
<LessonCard minutes={14} />
jsx
function LessonCard({ duration }) {
  return <p>{duration} min</p>;
}

duration is undefined because the props object has minutes, not duration.

Rename a property explicitly when the local name should differ.

jsx
function LessonCard({ minutes: duration }) {
  return <p>{duration} min</p>;
}

The public prop remains named minutes. Only the local binding is duration.

Missing Props Produce undefined

Render the component without minutes.

jsx
<LessonCard title="Props as Inputs" />

The object has no minutes property, so reading it produces undefined.

jsx
function LessonCard({ title, minutes }) {
  return <p>{title}, {minutes} min</p>;
}

React renders no text for the undefined expression. The page can display Props as Inputs, min without throwing an exception.

Choose a defined omitted behavior. A default can supply a normal value.

jsx
function LessonCard({ title, minutes = 10 }) {
  return <p>{title}, {minutes} min</p>;
}

The default applies when the prop is omitted or explicitly undefined.

jsx
<LessonCard title="Props" />
<LessonCard title="Props" minutes={undefined} />

It does not apply to null.

jsx
<LessonCard title="Props" minutes={null} />

null is an explicit supplied value. React renders no text for it in this position.

TypeScript can report static caller mismatches. Values from a server or storage still require runtime validation.

Boolean Shorthand Supplies true

A JSX prop written without a value receives true.

jsx
<LessonCard featured />

This has the same prop value as the explicit form.

jsx
<LessonCard featured={true} />

Omitting the prop gives the child undefined when it reads featured.

jsx
<LessonCard />

Supply false explicitly when the prop needs to be present with a false value.

jsx
<LessonCard featured={false} />

Use boolean names that read as conditions, such as featured, complete, disabled, and showDetails.

Props Are Read-Only Inputs

A component must not assign to its props object.

jsx
function LessonCard(props) {
  props.title = props.title.toUpperCase();
  return <h2>{props.title}</h2>;
}

The parent supplied the value. The child should calculate another local value.

jsx
function LessonCard({ title }) {
  const displayTitle = title.toUpperCase();
  return <h2>{displayTitle}</h2>;
}

The local binding exists only for this render. The supplied prop remains unchanged.

React's render model depends on components calculating from their current inputs without changing those inputs. A later parent render can supply another value through another element description.

jsx
<LessonCard title="Children as Composition" />

React calls the child with the next props object during that render. The child does not write into the old object to request the update.

Nested Objects Remain Shared References

Object props require another level of care.

jsx
const lesson = {
  title: "Props as Inputs",
  complete: false,
};

<LessonCard lesson={lesson} />

The props object contains a reference to lesson. React does not deep-copy that object.

This component mutates the parent's data.

jsx
function LessonCard({ lesson }) {
  lesson.complete = true;
  return <h2>{lesson.title}</h2>;
}

Any code holding the same object can observe the changed property. The mutation also occurred during render.

Read the object and derive output.

jsx
function LessonCard({ lesson }) {
  const label = lesson.complete ? "Complete" : "Not started";
  return <p>{lesson.title}, {label}</p>;
}

When completion changes, the parent supplies a new data value and the child renders from that value.

Special Props Do Not Reach the Component Normally

React uses key for list identity and does not pass it as an ordinary prop.

jsx
<LessonCard key={lesson.id} lesson={lesson} />

LessonCard cannot read props.key. Pass another prop if it needs the ID.

jsx
<LessonCard
  key={lesson.id}
  lessonId={lesson.id}
  lesson={lesson}
/>

React 19 makes ref available as a prop to function components, while React handles its attachment behavior according to the element and component. Add a ref only when code needs imperative access to a host node or another supported ref target.

Do Not Copy Props into State by Default

Copying an initial prop into state disconnects future prop changes from the displayed value.

jsx
function LessonCard({ title }) {
  const [localTitle] = useState(title);
  return <h2>{localTitle}</h2>;
}

The initial state uses the first title. A later parent prop does not automatically replace localTitle. The card can display old text while the parent supplies new text.

If the component only needs to display or calculate from a prop, read the prop directly.

jsx
function LessonCard({ title }) {
  return <h2>{title}</h2>;
}

Local state is appropriate when the value can change independently inside that component instance.

Build a Reusable Lesson Card

Define the card in its own module.

jsx
export default function LessonCard({ title, minutes, complete = false }) {
  const status = complete ? "Complete" : "Not started";

  return (
    <article>
      <h2>{title}</h2>
      <p>{minutes} min</p>
      <p>{status}</p>
    </article>
  );
}

Render two occurrences from App.

jsx
<LessonCard
  title="Props as Inputs"
  minutes={14}
  complete
/>
jsx
<LessonCard
  title="Children as Composition"
  minutes={12}
/>

The function code remains unchanged. Each element supplies the values for one card occurrence.

Check the Prop Path

Trace each prop from the parent expression to the component that reads it.

text
parent creates a component element
JSX records supplied prop values
React calls the component with one props object
the component reads without mutating
the returned UI follows those inputs
a later parent render can supply another props object

Source Notes