all topics

Props

What are props?

Whatever you pass into a component when you use it. React bundles all of it into a single object and hands that object to the function.

  • props is just a parameter name, not a keyword. You could call it data, movieData, anything. props is convention, not a requirement.
jsx
function MovieCard(props) {
    // ...
}

Calling a component passes an object

<MovieCard title="Dune" genre="Sci-Fi" rating={5} /> is basically calling MovieCard with one argument, an object built from the attributes you wrote.

jsx
<MovieCard title="Dune" genre="Sci-Fi" rating={5} />
 
// roughly equivalent to:
MovieCard({
    title: "Dune",
    genre: "Sci-Fi",
    rating: 5
})

Reading props

Two ways to get at them, same underlying object either way.

  • props.title reads a single value straight off the object.
  • Destructuring, { title, genre, rating }, pulls several out at once. It's the same as writing const title = props.title for each one, just more compact.
jsx
function MovieCard(props) {
    return <h2>{props.title}</h2>
}
 
function MovieCard({ title, genre, rating }) {
    return <h2>{title}</h2>
}

Props can hold any JS value

Curly braces mean "evaluate this as JavaScript", not "insert this text". That's the whole difference between rating={5} (the number 5) and rating="5" (the string "5").

jsx
<MovieCard title="Dune" />           // string
<MovieCard rating={5} />             // number
<MovieCard watched={true} />         // boolean
<MovieCard genres={["Sci-Fi"]} />    // array
<MovieCard movie={movie} />          // object
<MovieCard onDelete={deleteMovie} /> // function

There aren't really separate mechanisms here for "passing a value" versus "passing an expression". There's one mechanism, you pass named props to a component, and the value just happens to be able to be any JavaScript expression.

Passing a whole object as one prop

Instead of spreading a movie's fields across separate props, pass the whole object as a single prop.

jsx
const movie = {
    title: "Dune",
    genre: "Sci-Fi",
    rating: 5
}
 
<MovieCard movie={movie} />
  • The props object becomes { movie: { title: "Dune", genre: "Sci-Fi", rating: 5 } }, one key, movie, whose value happens to be another object.
  • Receive it the same way as any other prop, destructure { movie }, then read movie.title, movie.genre, movie.rating off it.
jsx
function MovieCard({ movie }) {
    return <h2>{movie.title}</h2>
}
  • movie={movie} is really just saying: the value of this one prop is an entire object.

No fixed shape

Props are just a plain object. Nothing enforces a contract on what's supposed to be in it.

  • Don't pass a prop the component reads → it's just undefined, same as reading any missing key off a JS object.
  • Pass extra props the component never reads → nothing breaks, they're simply ignored.
jsx
<MovieCard title="Dune" />
// props.rating is undefined
 
<MovieCard title="Dune" rating={5} director="Villeneuve" />
// director is passed along fine even though MovieCard never reads it

Data down, events up

Props only flow one direction: parent to child. A child that needs to affect the parent doesn't reach up directly, it calls a function the parent handed it as a prop.

jsx
// parent
<MovieCard onDelete={deleteMovie} />
 
// child
function MovieCard({ onDelete }) {
    return <button onClick={onDelete}>Delete</button>
}

The full cycle, parent owns the state, everything else follows from that:

  • Parent owns the state.
  • Parent passes data down to the child, as props.
  • Child displays it, user interacts with it.
  • Child calls the callback prop it was given.
  • Parent updates its state.
  • Parent passes the new data down.
  • Child re-renders.

The one mental model to remember

<MovieCard title="Dune" rating={5} /> becomes the object { title: "Dune", rating: 5 }, and that object is exactly what MovieCard(props) receives. Read it as props.title, or destructure it, same data either way. Props are just data and functions, handed down from parent to child.