React Passing Functions as Props

React components usually receive data from their parent through props. But props are not limited to strings, numbers, objects, and arrays. You can also pass functions as props.

Passing a function from a parent component to a child component allows the child to trigger behavior defined by the parent.

This is one of the most important patterns in React because it allows components to communicate while keeping data and state ownership organized.

For example, a parent component might have a button handler:

function App() {
  function handleClick() {
    console.log("Button clicked");
  }

  return <Button onClick={handleClick} />;
}

The handleClick function is passed to the Button component through the onClick prop.

The child can then call that function when the user interacts with the button.

The basic flow is:

Parent
   ↓
Function passed as prop
   ↓
Child
   ↓
Child calls function
   ↓
Parent-defined behavior runs

This pattern is often described as passing a callback function as a prop.

Why Pass Functions as Props?

A child component cannot directly access the parent’s local variables or functions.

Instead, the parent can explicitly give the child access to a function.

For example, suppose the parent has a message:

function App() {
  function showMessage() {
    alert("Welcome to Web4Learners!");
  }

  return <WelcomeButton onWelcome={showMessage} />;
}

The child receives onWelcome and can call it.

function WelcomeButton({ onWelcome }) {
  return (
    <button onClick={onWelcome}>
      Show Message
    </button>
  );
}

The child does not need to know how the function works.

It only needs to know that it has received a function it can call.

This creates a useful separation:

  • The parent defines the behavior.
  • The child provides the user interaction.
  • The function connects the two.

Passing a Function to a Child Component

Let’s start with a simple example.

function App() {
  function handleClick() {
    alert("Hello from the parent!");
  }

  return <ChildButton handleClick={handleClick} />;
}

function ChildButton({ handleClick }) {
  return (
    <button onClick={handleClick}>
      Click Me
    </button>
  );
}

export default App;

Here, handleClick is a function.

The parent passes it:

<ChildButton handleClick={handleClick} />

The child receives it:

function ChildButton({ handleClick })

The child then provides it to the button:

<button onClick={handleClick}>

When the button is clicked, React calls the function.

Function Props Are Values

A function can be passed as a prop just like other JavaScript values.

For example:

function App() {
  const message = "Hello";

  function showMessage() {
    console.log(message);
  }

  return (
    <MessageButton
      message={message}
      onShowMessage={showMessage}
    />
  );
}

The component receives two different props:

  • message contains a string.
  • onShowMessage contains a function.

The child can use both.

function MessageButton({ message, onShowMessage }) {
  return (
    <div>
      <p>{message}</p>

      <button onClick={onShowMessage}>
        Show Message
      </button>
    </div>
  );
}

Passing the Function vs Calling the Function

One of the most common mistakes is confusing a function reference with a function call.

When passing a function to an event handler, use:

<button onClick={handleClick}>
  Click
</button>

Do not write:

<button onClick={handleClick()}>
  Click
</button>

The first version gives React the function so React can call it when the click happens.

The second version calls the function while the component is rendering.

A useful mental model is:

onClick={handleClick}
             ↑
       give React the function

onClick={handleClick()}
             ↑
       call the function now

Passing a Function with Arguments

Sometimes you want the child to trigger a function with specific information.

For example, the parent may have a function that accepts a name.

function App() {
  function greet(name) {
    alert(`Hello, ${name}!`);
  }

  return (
    <button onClick={() => greet("Karim")}>
      Greet
    </button>
  );
}

export default App;

The arrow function creates a function that calls greet() when the button is clicked.

The same technique can be used when the function is passed to another component.

function App() {
  function greet(name) {
    alert(`Hello, ${name}!`);
  }

  return (
    <UserButton
      onGreet={() => greet("Karim")}
    />
  );
}

function UserButton({ onGreet }) {
  return (
    <button onClick={onGreet}>
      Greet User
    </button>
  );
}

export default App;

Passing Data from Child to Parent

This is where function props become especially useful.

React generally follows a parent-to-child data flow.

A child does not directly send a prop back to its parent.

Instead, the parent passes a function to the child. The child calls that function and can provide data as an argument.

For example:

function App() {
  function handleMessage(message) {
    console.log(message);
  }

  return <Child onSendMessage={handleMessage} />;
}

function Child({ onSendMessage }) {
  return (
    <button onClick={() => onSendMessage("Hello Parent!")}>
      Send Message
    </button>
  );
}

export default App;

The sequence is:

Parent creates function
        ↓
Parent passes function to Child
        ↓
Child calls the function
        ↓
Child provides data
        ↓
Parent receives the data

This is commonly used for child-to-parent communication.

Function Props and State

Function props are frequently used when the parent owns state.

Consider a counter.

The parent owns the state:

import { useState } from "react";

function App() {
  const [count, setCount] = useState(0);

  function increaseCount() {
    setCount((currentCount) => currentCount + 1);
  }

  return <CounterButton onIncrease={increaseCount} />;
}

function CounterButton({ onIncrease }) {
  return (
    <button onClick={onIncrease}>
      Increase
    </button>
  );
}

export default App;

The child does not need to own the counter state.

Instead:

  • App owns count.
  • App owns increaseCount.
  • App passes increaseCount to CounterButton.
  • CounterButton calls it when clicked.
  • The parent state updates.
  • React renders the updated UI.

This is a common React architecture.

Function Props Can Receive Child Data

The child can send information back through the callback.

For example, imagine a search component.

function App() {
  function handleSearch(query) {
    console.log("Search:", query);
  }

  return <SearchBox onSearch={handleSearch} />;
}

function SearchBox({ onSearch }) {
  return (
    <input
      onChange={(event) => {
        onSearch(event.target.value);
      }}
      placeholder="Search tutorials"
    />
  );
}

export default App;

Whenever the user types, the child calls onSearch() and passes the current input value.

The parent receives that value through handleSearch().

Function Props with Forms

Function props are also useful with forms.

The parent can define what should happen when a form is submitted.

function App() {
  function handleSubmit(username) {
    console.log("Submitted:", username);
  }

  return <LoginForm onSubmit={handleSubmit} />;
}

function LoginForm({ onSubmit }) {
  function handleFormSubmit(event) {
    event.preventDefault();

    const username = event.target.username.value;

    onSubmit(username);
  }

  return (
    <form onSubmit={handleFormSubmit}>
      <input
        name="username"
        placeholder="Username"
      />

      <button type="submit">
        Submit
      </button>
    </form>
  );
}

export default App;

Here, the form component handles the form interaction while the parent decides what to do with the submitted value.

Naming Function Props

There is no special rule requiring a function prop to have a particular name.

You could write:

<Button clickHandler={handleClick} />

However, React applications commonly use names beginning with on for callback props.

Examples include:

onClick
onSubmit
onChange
onDelete
onSave
onSelect
onClose
onSearch
onLogin
onAdd

For example:

<CourseCard
  course={course}
  onSelect={handleCourseSelect}
/>

The on... naming convention makes it easier to understand that the prop represents an action or callback.

Function Props and Event Handlers

A function prop does not have to be connected directly to a DOM event.

You can call it inside another function.

function Button({ onAction }) {
  function handleClick() {
    console.log("Preparing action...");

    onAction();
  }

  return (
    <button onClick={handleClick}>
      Perform Action
    </button>
  );
}

This allows the child to perform additional work before or after calling the parent-provided function.

Passing Multiple Function Props

A component can receive multiple callback functions.

function App() {
  function handleSave() {
    console.log("Saved");
  }

  function handleDelete() {
    console.log("Deleted");
  }

  return (
    <Editor
      onSave={handleSave}
      onDelete={handleDelete}
    />
  );
}

function Editor({ onSave, onDelete }) {
  return (
    <div>
      <button onClick={onSave}>
        Save
      </button>

      <button onClick={onDelete}>
        Delete
      </button>
    </div>
  );
}

This can be useful for reusable components with multiple user actions.

However, avoid giving a component an unnecessarily large number of callbacks. A component’s API should remain understandable.

Function Props with Objects

You can combine function props with object props.

For example:

function App() {
  const course = {
    id: 1,
    title: "React Tutorial"
  };

  function handleEnroll(course) {
    console.log("Enrolled in:", course.title);
  }

  return (
    <CourseCard
      course={course}
      onEnroll={handleEnroll}
    />
  );
}

function CourseCard({ course, onEnroll }) {
  return (
    <article>
      <h2>{course.title}</h2>

      <button onClick={() => onEnroll(course)}>
        Enroll
      </button>
    </article>
  );
}

export default App;

The child receives the course object and the callback function.

When the user clicks the button, the child passes the course back to the parent through the callback.

Function Props with Arrays

Function props can also be used with arrays.

For example, a parent might maintain a list of courses:

import { useState } from "react";

function App() {
  const [courses, setCourses] = useState([
    {
      id: 1,
      title: "React"
    },
    {
      id: 2,
      title: "JavaScript"
    }
  ]);

  function handleDelete(courseId) {
    setCourses((currentCourses) =>
      currentCourses.filter(
        (course) => course.id !== courseId
      )
    );
  }

  return (
    <CourseList
      courses={courses}
      onDelete={handleDelete}
    />
  );
}

function CourseList({ courses, onDelete }) {
  return (
    <div>
      {courses.map((course) => (
        <article key={course.id}>
          <h2>{course.title}</h2>

          <button onClick={() => onDelete(course.id)}>
            Delete
          </button>
        </article>
      ))}
    </div>
  );
}

export default App;

This example combines several concepts:

  • State
  • Arrays
  • Objects
  • Props
  • Function props
  • map()
  • filter()
  • Event handlers

The parent owns the array and the child requests the deletion by calling the callback.

Callback Props and Component Reusability

Function props make components more flexible.

Consider a reusable button.

Instead of hardcoding what the button should do, you can let the parent decide.

function ActionButton({ label, onAction }) {
  return (
    <button onClick={onAction}>
      {label}
    </button>
  );
}

Now different parents can use the same component for different actions.

function App() {
  function saveData() {
    console.log("Saving...");
  }

  function openSettings() {
    console.log("Opening settings...");
  }

  return (
    <div>
      <ActionButton
        label="Save"
        onAction={saveData}
      />

      <ActionButton
        label="Settings"
        onAction={openSettings}
      />
    </div>
  );
}

The ActionButton does not need to know what the action actually does.

That decision belongs to the parent.

Function Props and Conditional Actions

A callback can also be used based on a condition.

function UserCard({ user, onEdit }) {
  return (
    <div>
      <h2>{user.name}</h2>

      {user.canEdit && (
        <button onClick={() => onEdit(user.id)}>
          Edit
        </button>
      )}
    </div>
  );
}

The component decides whether to display the button, while the parent provides the editing behavior.

Remember that hiding a button in the UI is not a security mechanism. Authorization for sensitive operations must also be enforced where the operation is actually performed, such as on a backend.

Common Mistakes with Function Props

Calling the Function During Rendering

Avoid:

<Button onClick={handleClick()} />

Prefer:

<Button onClick={handleClick} />

Forgetting to Pass the Function

If the child expects onSave but the parent doesn’t provide it, calling it can produce an error.

You can design components to handle optional callbacks when appropriate.

function SaveButton({ onSave }) {
  function handleClick() {
    if (onSave) {
      onSave();
    }
  }

  return (
    <button onClick={handleClick}>
      Save
    </button>
  );
}

Whether a callback should be optional depends on the component’s design.

Using the Wrong Prop Name

If the parent writes:

<Button onSave={handleSave} />

The child must receive onSave.

function Button({ onSave }) {
  return (
    <button onClick={onSave}>
      Save
    </button>
  );
}

Using a different name accidentally will result in undefined unless the component is intentionally mapping the prop.

Mutating Parent Data

A callback should generally request an update rather than allowing a child to directly mutate data owned by its parent.

The parent should remain responsible for changing its own state.

Overusing Callback Props

Function props are useful, but not every component needs many callbacks.

If a component receives a large collection of unrelated callbacks and data, consider whether the component boundaries or data flow could be simplified.

Best Practices for Function Props

Keep these practices in mind:

  • Use descriptive callback names.
  • Prefer on... names for action callbacks.
  • Pass the function reference when React should call it later.
  • Use an arrow function when you need to provide arguments.
  • Keep state ownership in the appropriate parent component.
  • Let children communicate upward by calling callback props.
  • Keep callback responsibilities clear.
  • Avoid unnecessary callback props.
  • Avoid directly mutating data received through props.
  • Use stable component APIs that are easy to understand.

Practical Example: Web4Learners Tutorial Card

Let’s build a reusable tutorial card.

The parent owns the tutorials and provides a function for selecting a tutorial.

function App() {
  const tutorials = [
    {
      id: 1,
      title: "React Tutorial",
      category: "Frontend"
    },
    {
      id: 2,
      title: "JavaScript Tutorial",
      category: "Programming"
    },
    {
      id: 3,
      title: "CSS Tutorial",
      category: "Web Design"
    }
  ];

  function handleReadTutorial(tutorialId) {
    console.log(
      "Opening tutorial:",
      tutorialId
    );
  }

  return (
    <TutorialList
      tutorials={tutorials}
      onRead={handleReadTutorial}
    />
  );
}

function TutorialList({ tutorials, onRead }) {
  return (
    <div>
      {tutorials.map((tutorial) => (
        <TutorialCard
          key={tutorial.id}
          tutorial={tutorial}
          onRead={onRead}
        />
      ))}
    </div>
  );
}

function TutorialCard({ tutorial, onRead }) {
  return (
    <article>
      <h2>{tutorial.title}</h2>

      <p>
        Category: {tutorial.category}
      </p>

      <button
        onClick={() => onRead(tutorial.id)}
      >
        Read Tutorial
      </button>
    </article>
  );
}

export default App;

Notice how the responsibilities are separated.

App:

  • Owns the tutorial data.
  • Defines the handleReadTutorial function.

TutorialList:

  • Receives the tutorial array.
  • Creates a card for each tutorial.
  • Passes the callback down.

TutorialCard:

  • Displays one tutorial.
  • Calls onRead() when the button is clicked.

This is a practical example of how function props allow multiple components to work together without tightly coupling their responsibilities.

Understanding the Complete Data Flow

The previous example can be visualized like this:

App
│
├── tutorials
│
├── handleReadTutorial()
│
└── TutorialList
       │
       ├── tutorial
       ├── onRead
       │
       └── TutorialCard
              │
              └── User clicks button
                       │
                       ↓
                   onRead(id)
                       │
                       ↓
              handleReadTutorial(id)
                       │
                       ↓
                   App responds

The important point is that the function travels down as a prop, but calling the function allows information to travel back up to the parent.

Image Placement

Place image immediately after the “Data from Child to Parent” section.

Image description: A modern React data-flow infographic showing a parent component defining a callback function, passing it downward to a child through a prop, and the child calling the callback with data that travels back to the parent. Use a clean modern developer aesthetic with React-inspired visual elements, subtle gradients, rounded cards, arrows, and minimal text.

Caption: Passing a callback function from a parent to a child for child-to-parent communication.

Alt text: React callback prop showing a function passed from parent to child and data sent back to the parent.

Practice Exercise

Create a small React application for a list of tutorials.

Start with three tutorials:

const tutorials = [
  {
    id: 1,
    title: "React"
  },
  {
    id: 2,
    title: "JavaScript"
  },
  {
    id: 3,
    title: "CSS"
  }
];

Then create:

  • An App component.
  • A TutorialList component.
  • A TutorialCard component.
  • An onSelect function in App.
  • Pass onSelect to TutorialList.
  • Pass the callback to each TutorialCard.
  • Add a button to each card.
  • When the button is clicked, send the tutorial’s id to the parent.
  • Display the selected tutorial ID in the parent.

As an additional challenge, use useState to store the selected tutorial and display its title.

Function Props Cheat Sheet

ConceptExample
Define functionfunction handleClick() {}
Pass function<Button onClick={handleClick} />
Receive functionfunction Button({ onClick })
Use callback<button onClick={onClick}>
Pass argumentsonClick={() => handleDelete(id)}
Callback with dataonSelect(item)
Common namingonSave, onDelete, onSelect
Parent owns stateconst [items, setItems] = useState([])
Child requests updateonDelete(item.id)

Key Takeaways

  • Functions can be passed to React components through props.
  • Function props are often called callback props.
  • The parent can define behavior and give the child a way to trigger it.
  • Pass a function reference when React should call it later.
  • Use an arrow function when you need to pass arguments.
  • Callback props are commonly used for child-to-parent communication.
  • The parent can pass a callback downward, and the child can call it with data.
  • Function props work especially well with state, arrays, objects, forms, and reusable components.
  • Use descriptive names such as onSave, onDelete, onSelect, and onSearch.
  • Keep state ownership clear and avoid directly mutating parent-owned data.

The core pattern to remember is:

function Parent() {
  function handleAction(data) {
    console.log(data);
  }

  return (
    <Child onAction={handleAction} />
  );
}

function Child({ onAction }) {
  return (
    <button onClick={() => onAction("Hello Parent")}>
      Send Data
    </button>
  );
}

The parent passes the function down, and the child calls the function when it needs to communicate with the parent.

This pattern is fundamental to understanding React’s component communication and becomes especially important when working with state and reusable components.

Leave a Comment