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:
messagecontains a string.onShowMessagecontains 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:
Appownscount.AppownsincreaseCount.ApppassesincreaseCounttoCounterButton.CounterButtoncalls 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
handleReadTutorialfunction.
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
Appcomponent. - A
TutorialListcomponent. - A
TutorialCardcomponent. - An
onSelectfunction inApp. - Pass
onSelecttoTutorialList. - Pass the callback to each
TutorialCard. - Add a button to each card.
- When the button is clicked, send the tutorial’s
idto 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
| Concept | Example |
|---|---|
| Define function | function handleClick() {} |
| Pass function | <Button onClick={handleClick} /> |
| Receive function | function Button({ onClick }) |
| Use callback | <button onClick={onClick}> |
| Pass arguments | onClick={() => handleDelete(id)} |
| Callback with data | onSelect(item) |
| Common naming | onSave, onDelete, onSelect |
| Parent owns state | const [items, setItems] = useState([]) |
| Child requests update | onDelete(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, andonSearch. - 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.