React Props Deep Dive

Props are how React components receive data from their parents. The word "props" is short for "properties." They work like settings you pass to a component when you use it — similar to how you fill out a form with different details each time. This topic covers props in detail, including default props, prop types, passing functions, and the children prop.

What Props Are

Props flow downward — from a parent component to a child component. The child reads the props but cannot change them. This one-way flow keeps data predictable and easy to trace.

Diagram: Props flow down like water

         App (parent)
          |
    name="Alice"    age={30}
          |
        Profile (child)
          |
       Shows "Alice, 30"

Passing and Reading Props

// Parent passes props
function App() {
  return <Profile name="Alice" age={30} active={true} />;
}

// Child receives props
function Profile(props) {
  return (
    <div>
      <h2>{props.name}</h2>
      <p>Age: {props.age}</p>
      <p>Status: {props.active ? "Active" : "Inactive"}</p>
    </div>
  );
}

String props use regular quotes. Numbers, booleans, objects, arrays, and functions use curly braces.

Destructuring Props

Instead of writing props.name everywhere, destructure props directly in the function signature. The code gets shorter and easier to scan.

// Without destructuring
function Profile(props) {
  return <h2>{props.name}</h2>;
}

// With destructuring (preferred)
function Profile({ name, age, active }) {
  return (
    <div>
      <h2>{name}</h2>
      <p>Age: {age}</p>
    </div>
  );
}

Default Props

You can set default values for props in case the parent does not pass them. This prevents the component from breaking when a prop is missing.

Method 1: Default values in destructuring (recommended)

function Button({ label = "Click me", color = "blue" }) {
  return <button style={{ background: color }}>{label}</button>;
}

// Used without props — defaults kick in
<Button />           // renders: "Click me" in blue
<Button label="Go" /> // renders: "Go" in blue

Method 2: defaultProps (older style, still works)

function Button({ label, color }) {
  return <button style={{ background: color }}>{label}</button>;
}

Button.defaultProps = {
  label: "Click me",
  color: "blue",
};

Passing Functions as Props

Props are not limited to data. You can pass functions as props. This is how child components communicate back to parents — the child calls a function that the parent provided.

Diagram: Child talks to parent via a callback prop

Parent
  |
  | passes: onDelete={handleDelete}
  |
Child
  |
  | user clicks delete button
  | calls: onDelete(item.id)
  |
Parent
  |
  | handleDelete runs, updates state
function App() {
  const handleDelete = (id) => {
    console.log("Delete item with id:", id);
  };

  return <ItemCard id={5} onDelete={handleDelete} />;
}

function ItemCard({ id, onDelete }) {
  return (
    <div>
      <p>Item #{id}</p>
      <button onClick={() => onDelete(id)}>Delete</button>
    </div>
  );
}

Passing Objects and Arrays as Props

You can pass any JavaScript value as a prop, including objects and arrays.

const user = {
  name: "Sam",
  role: "Admin",
  skills: ["React", "Node", "CSS"]
};

// Passing the whole object
<UserCard user={user} />

// Inside UserCard
function UserCard({ user }) {
  return (
    <div>
      <h3>{user.name} – {user.role}</h3>
      <ul>
        {user.skills.map(skill => <li key={skill}>{skill}</li>)}
      </ul>
    </div>
  );
}

The Spread Operator with Props

When a parent has an object and wants to pass all its properties as props, use the spread operator.

const btnProps = {
  label: "Submit",
  color: "green",
  disabled: false
};

// Instead of writing each prop manually:
<Button label={btnProps.label} color={btnProps.color} disabled={btnProps.disabled} />

// Use spread:
<Button {...btnProps} />

Use spread carefully — avoid passing unneeded props to DOM elements, as it causes warnings.

The children Prop

React provides a special built-in prop called children. Whatever you place between a component's opening and closing tags becomes the children prop.

function Card({ children }) {
  return <div className="card">{children}</div>;
}

// Using Card with content inside
<Card>
  <h3>Product Title</h3>
  <p>Description goes here.</p>
</Card>

The Card component wraps whatever you put inside it. This makes components reusable as layout containers.

Props Are Read-Only

A child component must never modify its own props. Props belong to the parent. This rule keeps data flow predictable.

function BadExample({ name }) {
  name = "Different Name"; // WRONG — never modify props
  return <p>{name}</p>;
}

If the child needs to change a value, the parent should hold that value in state and pass a function to update it.

Prop Drilling: A Common Challenge

When you need to pass a prop through many levels of components just to reach a deeply nested child, it is called prop drilling.

App → Layout → Sidebar → UserMenu → UserName

Passing the userName prop through all four intermediate components just to reach UserName is tedious. The Context API and state management tools like Redux solve this problem — covered in later topics.

Summary

Props are read-only data passed from parent to child. They can carry strings, numbers, booleans, objects, arrays, or functions. Destructuring props in the function signature makes code cleaner. Default prop values prevent crashes when props are missing. The children prop lets you wrap content inside a component. Function props let children communicate back to parents. Props always flow downward — if data needs to go up, use callback functions passed as props.

Leave a Comment

Your email address will not be published. Required fields are marked *