EduFlow
React Fundamentals
0%

Section 2 · Components & Props

Props In Depth

21 min3 min readCode playground

Lesson at a glance

Pass data into components with props, destructure them with defaults, choose between passing objects and single values, and treat props as read-only snapshots.

  1. 1Passing and reading props
  2. 2Default values
  3. 3Objects or single values?
  4. 4Props are read-only snapshots
  5. 5Passing functions
  6. 6ref is just a prop now

No video here — just the real thing: a written lesson, runnable examples, a quiz and practice.

Your MovieCard from Section 1 always says “Arrival”. A component that can only show one movie isn’t reusable, it’s a template with one use. Props fix that: they are how a parent hands data to a child, in the same way arguments are how you hand data to a function.

Passing and reading props

Pass props as attributes on the component’s tag:

src/App.jsx
export default function App() {
  return (
    <main>
      <MovieCard title="Arrival" year={2016} watched />
      <MovieCard title="Past Lives" year={2023} watched={false} />
    </main>
  );
}

React collects them into one object, { title: 'Arrival', year: 2016, watched: true }, and passes it as the component’s first argument. You almost always destructure it right in the parameter list:

src/components/MovieCard.jsx
export default function MovieCard({ title, year, watched }) {
  return (
    <article>
      <h2>{title}</h2>
      <p>{year}</p>
      <p>{watched ? 'Watched' : 'To watch'}</p>
    </article>
  );
}

Three syntax details trip people up:

  • Quotes mean string. year="2016" passes the string “2016”. Use braces for anything else: year={2016}, watched={false}, genres={['drama', 'sci-fi']}.
  • A bare attribute means true. <MovieCard watched /> is the same as watched={true}, like disabled in HTML.
  • Destructuring is optional. function MovieCard(props) with props.title works too. Destructuring documents which props the component expects at a glance, which is why most code uses it.

Default values

Give a prop a default in the destructuring pattern:

JSX
export default function MovieCard({ title, year, watched = false, genres = [] }) {
  return (
    <article>
      <h2>{title}</h2>
      <p>{year} · {genres.join(', ') || 'No genres yet'}</p>
      <p>{watched ? 'Watched' : 'To watch'}</p>
    </article>
  );
}

Now <MovieCard title="Dune" year={2021} /> works without crashing on genres.join. Defaults apply when a prop is missing or undefined, but not when it’s null. If your data can contain null, handle it explicitly, for example with genres ?? [].

Objects or single values?

When a component is about one thing, pass that thing:

JSX
<MovieCard movie={movie} />

function MovieCard({ movie }) {
  return <h2>{movie.title}</h2>;
}

This keeps call sites short and the component’s purpose obvious. You’ll also see spreading, <MovieCard {...movie} />, which passes every field as its own prop. It’s convenient, but it hides what the component really uses and passes along fields it doesn’t need. Use it sparingly, typically in small wrapper components that forward props to an element.

Passing single values has its own advantage: a component that takes title and year can show a movie, a book or a podcast episode. Choose based on how specific the component is. MovieCard is about movies, so movie={movie} is natural. A generic Card should take plain values.

Props are read-only snapshots

A component must never change its own props. Props belong to the parent’s render: they’re a snapshot of what the parent decided at that moment. In development React even freezes the props object, so assigning to it throws an error.

So how does a card ever change? The parent renders it again with different props. When App later has the movies in state and one gets marked watched, App re-renders and passes watched={true} to that card. The card doesn’t update itself; it’s handed new data. That one-way flow, data down from parent to child, is what makes React apps predictable: to find out why a card shows something, you look up the tree.

Passing functions

Props can be any value, including functions. That’s how a child tells its parent something happened:

JSX
<MovieCard movie={movie} onToggle={() => toggleWatched(movie.id)} />

The on prefix is a naming convention for callback props, matching DOM events like onClick. You’ll use this pattern constantly in Section 3, where App owns the state and children ask it to change.

ref is just a prop now

Sometimes a parent needs the actual DOM node inside a child, for example to focus the title input after adding a movie. In React 19 a function component receives ref like any other prop and passes it to an element:

JSX
function TitleInput({ ref, ...rest }) {
  return <input ref={ref} {...rest} />;
}

Older code wraps components in forwardRef to do this. You don’t need it in React 19, and it’s expected to be deprecated. One prop is still special: key, which you’ll meet with lists, is read by React and never reaches your component.

Props let a parent configure a child with data. Next, you’ll see how to pass whole chunks of JSX into a component with children, and how that builds flexible layouts.

Key takeaways

  • Props are the arguments of a component; React passes them as a single object that you usually destructure in the parameter list.
  • Strings can be passed in quotes; every other value, including numbers and booleans, goes in curly braces.
  • Default values in destructuring (watched = false) apply only when the prop is missing or undefined.
  • A component must never change its props; to change what’s shown, the parent passes new props.
  • In React 19, ref is an ordinary prop on function components, so forwardRef is no longer needed.

Further reading

Check your understanding

4 questions

Question 1 of 4

What does MovieCard receive for year here? <MovieCard title="Arrival" year="2016" />

Question 2 of 4

MovieCard is declared as function MovieCard({ title, watched = false }). The parent renders <MovieCard title="Dune" watched={null} />. What is watched inside the component?

Question 3 of 4

This component is meant to show the year as “(2016)”. What’s wrong with it?

JSX
function MovieCard(props) {
  props.year = `(${props.year})`;
  return <p>{props.title} {props.year}</p>;
}
Question 4 of 4

You have a movie object with id, title, year and watched. MovieItem only displays the title and the watched status. Which call is the clearest default?

Practice

Code playground

Make MovieCard reusable

Every card says “Arrival”. Give MovieCard three props, title, year and watched, and use them:

  • <h2> shows the title, a <p> shows the year.
  • Another <p> shows Watched when watched is true and To watch otherwise. Default watched to false.

Then render these three cards in App, in this order:

title year watched
Arrival 2016 true
Past Lives 2023 (don’t pass it)
Paddington 2 2017 false
function MovieCard() {
  return (
    <article>
      <h2>Arrival</h2>
      <p>2016</p>
      <p>To watch</p>
    </article>
  );
}

export default function App() {
  return (
    <main>
      <MovieCard />
      <MovieCard />
      <MovieCard />
    </main>
  );
}
Loading the editor…

Ctrl/⌘ + Enter to run · Runs in a sandbox in your browser. Nothing leaves your device.

Preview

Console

(no output)

Checks

  • Three cards titled Arrival, Past Lives and Paddington 2, in that order
  • Each card shows its own year
  • Arrival shows “Watched”
  • Past Lives and Paddington 2 show “To watch”