Lesson 4 of 12

React Props

In the React Components lesson, every Dish showed the same text. A component is far more useful when you can give it different data each time you use it. That is what React props (short for properties) do. Props are to components what arguments are to functions, and they look just like HTML attributes.

Passing and reading React props

You pass props by writing attributes on the component tag: <Dish name="Masala dosa" price={90} />. Text can go in quotes; any other value – a number, a variable, an array, an object – goes in curly braces. React collects all the attributes into one object and passes it to your function as its first argument.

Most developers read props with destructuring right in the parameter list. Instead of function Dish(props) and then props.name, you write function Dish({ name, price }) and use name and price directly. It also documents, at a glance, which props the component expects.

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="utf-8">
  <title>One component, different data</title>
  <script src="https://unpkg.com/@babel/[email protected]/babel.min.js"></script>
  <style>
    body { font-family: system-ui, sans-serif; padding: 16px; line-height: 1.5; }
    .menu { display: flex; gap: 12px; flex-wrap: wrap; }
    .dish { border: 1px solid #ddd; border-radius: 8px; padding: 8px 12px; width: 170px; }
    .veg { color: #0a7a3d; } .nonveg { color: #b02a37; }
  </style>
</head>
<body>
  <div id="root"></div>

  <script type="text/babel" data-type="module">
import React from "https://esm.sh/[email protected]";
import { createRoot } from "https://esm.sh/[email protected]/client";

function Dish({ name, price, isVeg }) {
  return (
    <div className="dish">
      <strong>{name}</strong>
      <p>₹{price}</p>
      <p className={isVeg ? "veg" : "nonveg"}>{isVeg ? "Veg" : "Non-veg"}</p>
    </div>
  );
}

function App() {
  return (
    <div className="menu">
      <Dish name="Masala dosa" price={90} isVeg={true} />
      <Dish name="Chettinad chicken" price={260} isVeg={false} />
      <Dish name="Curd rice" price={70} isVeg />
    </div>
  );
}

createRoot(document.getElementById("root")).render(<App />);
  </script>
</body>
</html>

Notice the last dish: writing isVeg on its own is a shortcut for isVeg={true}. Also notice that price={90} passes a number, while price="90" would pass a string – an important difference when you do maths with it.

Default values

If a prop is not passed, its value is undefined. You can give a fallback with JavaScript’s default parameter syntax inside the destructuring: function Avatar({ size = 48 }). The default is used only when the prop is missing or undefined, not when it is 0 or an empty string.

Passing objects, arrays and functions

React props can carry any JavaScript value. Passing a whole object is common when the data comes from an API. You can also pass functions, which is how a child component tells its parent that something happened – you will use this pattern in React Events.

const student = { name: "Divya", course: "UI/UX", city: "Coimbatore" };

<ProfileCard
  person={student}                 // an object
  skills={["Figma", "HTML", "CSS"]}  // an array
  size={64}                        // a number
  onSelect={() => console.log("selected")}  // a function
/>

function ProfileCard({ person, skills, size = 48, onSelect }) {
  return (
    <div onClick={onSelect}>
      <h3>{person.name} – {person.course}</h3>
      <p>{skills.join(", ")}</p>
    </div>
  );
}

If you already have an object whose keys match the prop names, the spread syntax <ProfileCard {...student} /> passes each key as a separate prop. It is handy, but use it sparingly: explicit props make it clearer what a component receives.

The children prop

Whatever you put between the opening and closing tags of a component arrives in a special prop called children. This lets you build wrapper components – cards, panels, modals, layouts – that do not need to know what goes inside them.

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="utf-8">
  <title>Wrapper components with children</title>
  <script src="https://unpkg.com/@babel/[email protected]/babel.min.js"></script>
  <style>
    body { font-family: system-ui, sans-serif; padding: 16px; line-height: 1.5; }
    .card { border: 1px solid #ccc; border-radius: 10px; padding: 12px 16px; margin-bottom: 12px; max-width: 360px; }
    .card h3 { margin-top: 0; }
    .tip { border-left: 4px solid #f4a100; background: #fff8e6; }
  </style>
</head>
<body>
  <div id="root"></div>

  <script type="text/babel" data-type="module">
import React from "https://esm.sh/[email protected]";
import { createRoot } from "https://esm.sh/[email protected]/client";

function Card({ title, highlight = false, children }) {
  return (
    <section className={highlight ? "card tip" : "card"}>
      <h3>{title}</h3>
      {children}
    </section>
  );
}

function App() {
  return (
    <div>
      <Card title="Batch timings">
        <p>Weekday batch: 7 pm to 9 pm</p>
        <p>Weekend batch: Saturday 10 am</p>
      </Card>

      <Card title="Before you join" highlight>
        <ul>
          <li>Bring a laptop</li>
          <li>Install a code editor</li>
        </ul>
      </Card>
    </div>
  );
}

createRoot(document.getElementById("root")).render(<App />);
  </script>
</body>
</html>

React props are read-only

A component must never change its own props. Writing price = price * 2 or props.name = "x" inside a component breaks the “keep components pure” rule. Props belong to the parent: the parent decides what to pass, and when the parent passes new values, React renders the child again with them.

Data in React flows one way: from parent to child through props. When something needs to change over time – a counter, the text in a search box, the items in a cart – you need state, which is the topic of React State with useState.

Read-only does not mean props never change. When a parent renders again with different values – say, a new price after a discount – the child simply receives the new props and React renders it again. A helpful way to picture it: a component is a function of its props. Give it the same props and it shows the same thing; give it new props and it shows the new result.

Watch out: The key prop, which you will meet in Lists and Keys, is special: React uses it itself and never passes it to your component, so if a child needs the id, pass it again under another name, such as id={item.id}.

SituationHow to write it
Pass text<Tag label="Sale" />
Pass a number or variable<Tag count={3} />
Pass true<Tag active />
Read propsfunction Tag({ label, count })
Default valuefunction Tag({ count = 0 })
Nested content<Tag>Hello</Tag> → children

Further reading

Key takeaways

  • Props pass data from a parent component to a child, like function arguments.
  • Quote text props; use curly braces for numbers, variables, objects, arrays and functions.
  • Destructure props in the parameter list and give defaults with = value.
  • Content between a component's tags arrives as the children prop.
  • Props are read-only; use state for values that change over time.

Quick quiz

Check what you learned – pick an answer.

1. How do you pass the number 5 as a prop called qty?

2. In function Badge({ color = "blue" }), when is "blue" used?

3. Where does the text "Hello" end up in <Box>Hello</Box>?

Practice exercise

Create a TeamMember component that takes name, role and city props, with city defaulting to "Pondicherry". Render three team members from App, leaving out the city for one of them.

Show a hint

Use destructuring with a default: function TeamMember({ name, role, city = "Pondicherry" }).

Show the solution
<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="utf-8">
  <title>Team members</title>
  <script src="https://unpkg.com/@babel/[email protected]/babel.min.js"></script>
  <style>
    body { font-family: system-ui, sans-serif; padding: 16px; line-height: 1.5; }
    .member { border-bottom: 1px solid #ddd; padding: 6px 0; }
  </style>
</head>
<body>
  <div id="root"></div>

  <script type="text/babel" data-type="module">
import React from "https://esm.sh/[email protected]";
import { createRoot } from "https://esm.sh/[email protected]/client";

function TeamMember({ name, role, city = "Pondicherry" }) {
  return (
    <div className="member">
      <strong>{name}</strong> – {role} ({city})
    </div>
  );
}

function App() {
  return (
    <div>
      <h2>Our team</h2>
      <TeamMember name="Anitha" role="UX designer" city="Bengaluru" />
      <TeamMember name="Rahul" role="Front-end developer" city="Kochi" />
      <TeamMember name="Selvi" role="Content writer" />
    </div>
  );
}

createRoot(document.getElementById("root")).render(<App />);
  </script>
</body>
</html>

Progress is saved in this browser only.