Skip to content

React Tutorial Props and Data

Passing Functions as Props in React

Lesson 35 of 156 2 min read By Deepak Paswan Updated Oct 10, 2026

Runnable lesson · press Try it on the examples to edit and run them here.

Props flow down from parent to child. To send information up, the parent passes a function, and the child calls it. This is how forms, lists and buttons talk to the rest of your app.

The pattern

  1. The parent defines a function that knows what to do (for example, update state).
  2. The parent passes it down as a prop (by convention named onSomething).
  3. The child calls it when something happens, and can pass data as arguments.
App.jsx
JSX
function AddButton({ onAdd }) {
  return <button onClick={onAdd}>Add 1</button>;
}

function App() {
  const [total, setTotal] = React.useState(0);

  function handleAdd() {
    setTotal(total + 1);
  }

  return (
    <div>
      <p>Total: {total}</p>
      <AddButton onAdd={handleAdd} />
    </div>
  );
}

AddButton knows nothing about total. It only knows “when I am clicked, call onAdd“. That makes it reusable anywhere.

Sending data back to the parent

The child calls the function with arguments, and the parent receives them:

App.jsx
JSX
function NameList({ names, onSelect }) {
  return (
    <ul>
      {names.map((name) => (
        <li key={name}>
          <button onClick={() => onSelect(name)}>{name}</button>
        </li>
      ))}
    </ul>
  );
}

function App() {
  const [chosen, setChosen] = React.useState("nobody yet");
  return (
    <div>
      <p>Chosen: {chosen}</p>
      <NameList names={["Ada", "Grace", "Linus"]} onSelect={setChosen} />
    </div>
  );
}

Here the parent even passes setChosen directly. Note onClick={() => onSelect(name)}: we wrap the call in an arrow function so it runs on click, not during rendering.

Pass the function, do not call it

This is the number one mistake with function props:

App.jsx
JSX
<button onClick={handleClick}>OK</button>     // correct: passes the function
<button onClick={handleClick()}>OK</button>   // wrong: calls it while rendering
<button onClick={() => handleClick(id)}>OK</button> // correct: wraps a call that needs arguments

Writing handleClick() runs the function immediately during rendering, and its return value (usually undefined) becomes the click handler. If it also sets state, you get an endless loop of re-renders and an error.

Naming

  • The prop a component accepts starts with on: onSave, onDelete, onSelect.
  • The function the parent writes starts with handle: handleSave.
App.jsx
JSX
<ProductForm onSave={handleSave} />

Lifting state up

When two sibling components must share data, keep the state in their common parent and pass values down and functions down. That pattern is called lifting state up, and it is the subject of Lifting State Up.

A complete example: a deletable list

App.jsx
JSX
function Item({ name, onDelete }) {
  return (
    <li>
      {name} <button aria-label={"Delete " + name} onClick={() => onDelete(name)}>Delete {name}</button>
    </li>
  );
}

function App() {
  const [items, setItems] = React.useState(["Milk", "Eggs", "Bread"]);

  function handleDelete(name) {
    setItems(items.filter((item) => item !== name));
  }

  return (
    <ul>
      {items.map((item) => (
        <Item key={item} name={item} onDelete={handleDelete} />
      ))}
    </ul>
  );
}

App owns the list. Item just reports “delete me” and App decides what that means.

Key points

  • To communicate from child to parent, pass a function as a prop and call it in the child.
  • Pass the function itself (onClick={handle}), not its result (onClick={handle()}).
  • Use onX for props and handleX for the functions you define.