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.
- 1Passing and reading props
- 2Default values
- 3Objects or single values?
- 4Props are read-only snapshots
- 5Passing functions
- 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:
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:
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 aswatched={true}, likedisabledin HTML. - Destructuring is optional.
function MovieCard(props)withprops.titleworks 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:
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:
<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:
<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:
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 orundefined. - A component must never change its props; to change what’s shown, the parent passes new props.
- In React 19,
refis an ordinary prop on function components, soforwardRefis no longer needed.