In React, data usually flows downwards from a parent component to a child component through props.
While passing strings or numbers is easy, there are many cases where you need to pass a function to a child component.
I have spent over eight years building complex React applications, and I can tell you that mastering this “callback” pattern is essential for any developer.
In this tutorial, I will show you exactly how to pass functions to child components to handle events, update state, and communicate between parts of your app.
Pass a Function to a Child
When I first started with React, I wondered why a child component couldn’t just change the parent’s state directly.
The reason is “One-Way Data Flow.” React keeps things predictable by ensuring that only the component that owns the state can change it.
By passing a function (often called a callback) from the parent to the child, you give the child a “remote control” to trigger actions back in the parent.
This is commonly used for handling button clicks, form submissions, or updating a list of items based on user interaction.
Method 1: Pass a Simple Function via Props
The most common way I pass functions is through a standard prop. This is perfect for simple actions like clicking a button.
Imagine you are building a “Tip Calculator” for a New York City restaurant. The parent component handles the calculation, while the child component is the button.
Here is the full code for this approach:
import React, { useState } from 'react';
// The Child Component
const TipButton = ({ onCalculate }) => {
return (
<div style={{ marginTop: '20px' }}>
<button
onClick={onCalculate}
style={{ padding: '10px 20px', backgroundColor: '#007bff', color: '#fff', border: 'none', borderRadius: '5px', cursor: 'pointer' }}
>
Calculate 20% Tip
</button>
</div>
);
};
// The Parent Component
const RestaurantBill = () => {
const [billAmount, setBillAmount] = useState(100);
const [tip, setTip] = useState(0);
// This function will be passed to the child
const calculateTip = () => {
const calculatedTip = billAmount * 0.20;
setTip(calculatedTip);
};
return (
<div style={{ padding: '30px', fontFamily: 'Arial, sans-serif' }}>
<h1>NYC Dining Tip Tracker</h1>
<p>Your Bill: <strong>${billAmount}</strong></p>
<p>Recommended Tip: <strong>${tip}</strong></p>
{/* Passing the function as a prop named 'onCalculate' */}
<TipButton onCalculate={calculateTip} />
</div>
);
};
export default RestaurantBill;You can refer to the screenshot below to see the output.

In this example, I created a function called calculateTip in the parent. I then passed it to the TipButton component.
When the user clicks the button in the child, it triggers the function sitting in the parent.
Method 2: Pass Functions with Arguments
Sometimes, the child component needs to send data back up to the parent. This happens often in forms or search bars.
Suppose you are building a “State Tax Selector” where a user chooses a US state, and the parent component updates the tax rate.
In this case, the child needs to pass the selected value back into the function.
Here is the full code:
import React, { useState } from 'react';
// The Child Component
const StateSelector = ({ onStateChange }) => {
const handleSelect = (event) => {
// We call the function and pass the selected value as an argument
onStateChange(event.target.value);
};
return (
<div style={{ marginBottom: '20px' }}>
<label htmlFor="state-select">Select Your State: </label>
<select id="state-select" onChange={handleSelect} style={{ marginLeft: '10px', padding: '5px' }}>
<option value="">--Choose a State--</option>
<option value="California">California</option>
<option value="Texas">Texas</option>
<option value="Florida">Florida</option>
</select>
</div>
);
};
// The Parent Component
const TaxCalculator = () => {
const [selectedState, setSelectedState] = useState('');
// This function receives a value from the child
const updateTaxLocation = (stateName) => {
setSelectedState(stateName);
};
return (
<div style={{ padding: '30px', border: '1px solid #ddd', maxWidth: '400px' }}>
<h2>US Sales Tax Portal</h2>
<StateSelector onStateChange={updateTaxLocation} />
{selectedState && (
<div style={{ marginTop: '20px', padding: '10px', backgroundColor: '#f9f9f9' }}>
Configuring tax rates for: <strong>{selectedState}</strong>
</div>
)}
</div>
);
};
export default TaxCalculator;You can refer to the screenshot below to see the output.

I find this method incredibly useful when dealing with lists. The child can tell the parent which specific item was clicked by passing an ID.
Method 3: Use Anonymous Functions in Props
If you have a very short function, you might not want to define a separate named function in your component.
I often use anonymous arrow functions directly in the prop declaration for quick state toggles.
Let’s look at a “Newsletter Subscription” toggle example common for US-based marketing sites.
import React, { useState } from 'react';
// Child Component
const SubscriptionToggle = ({ toggleAction, status }) => {
return (
<button onClick={toggleAction} style={{ cursor: 'pointer' }}>
{status ? 'Unsubscribe from Weekly Updates' : 'Subscribe to Newsletter'}
</button>
);
};
// Parent Component
const MarketingDashboard = () => {
const [isSubscribed, setIsSubscribed] = useState(false);
return (
<div style={{ padding: '20px' }}>
<h3>User Preferences</h3>
<p>Status: {isSubscribed ? 'Active' : 'Inactive'}</p>
{/* Passing an anonymous function directly */}
<SubscriptionToggle
status={isSubscribed}
toggleAction={() => setIsSubscribed(!isSubscribed)}
/>
</div>
);
};
export default MarketingDashboard;You can refer to the screenshot below to see the output.

While this is convenient, be careful. If you pass a new anonymous function on every render, it can occasionally lead to performance issues in very large apps.
Method 4: Pass Functions Through Multiple Levels (Prop Drilling)
In larger projects, you might need to pass a function through several layers of components. This is known as “Prop Drilling.”
While I usually prefer using Context API or Redux for very deep trees, understanding how to drill props is a fundamental skill.
Here is an example of a “Silicon Valley Tech Stack” selector where the action moves through three levels.
import React, { useState } from 'react';
// Grandchild Component
const TechButton = ({ techName, onSelect }) => (
<button onClick={() => onSelect(techName)} style={{ margin: '5px' }}>
{techName}
</button>
);
// Child Component (The intermediary)
const TechList = ({ onSelectTech }) => (
<div style={{ padding: '10px', backgroundColor: '#eee' }}>
<h4>Available Technologies</h4>
<TechButton techName="React" onSelect={onSelectTech} />
<TechButton techName="Node.js" onSelect={onSelectTech} />
<TechButton techName="Python" onSelect={onSelectTech} />
</div>
);
// Parent Component
const JobApplication = () => {
const [selectedTech, setSelectedTech] = useState('None');
const handleSelection = (tech) => {
setSelectedTech(tech);
};
return (
<div style={{ padding: '20px' }}>
<h1>Engineering Job Portal</h1>
<p>Selected Skill: <strong>{selectedTech}</strong></p>
<hr />
{/* Drilling the handleSelection function down */}
<TechList onSelectTech={handleSelection} />
</div>
);
};
export default JobApplication;In this setup, the TechList component doesn’t actually use the onSelectTech function itself; it just passes it down to the TechButton.
Performance Optimization with useCallback
As an experienced developer, I have to mention performance. Every time a parent component re-renders, any function defined inside it is re-created.
If you pass that function to a child that is wrapped in React.memo, the child will re-render even if its data hasn’t changed, because the function “reference” is new.
To prevent this, I use the useCallback hook.
import React, { useState, useCallback } from 'react';
const HeavyChild = React.memo(({ action }) => {
console.log("Child rendered!");
return <button onClick={action}>Click Me</button>;
});
const OptimizedParent = () => {
const [count, setCount] = useState(0);
// useCallback ensures the function reference stays the same
const memoizedAction = useCallback(() => {
console.log("Action triggered");
}, []); // Empty dependency array means it never changes
return (
<div>
<p>Count: {count}</p>
<button onClick={() => setCount(count + 1)}>Increment Count</button>
<HeavyChild action={memoizedAction} />
</div>
);
};By using useCallback, the HeavyChild won’t re-render when the count changes, because the memoizedAction stays identical.
Common Issues to Avoid
I have seen many developers struggle with these two common mistakes:
- Forgetting to call the function: In the child component, make sure you use onClick={props.myFunction} and not onClick={props.myFunction()}. The second one calls the function immediately during rendering.
- Naming Confusion: I always recommend naming youhttps://pythonguides.com/react-component-re-render/r function props starting with “on” (like onDelete or onUpdate). This makes it clear that the prop expects a callback function.
Passing functions is the backbone of interactivity in React. Whether you are building a simple toggle or a complex US-based e-commerce checkout, these patterns will serve you well.
I have found that keeping functions focused and naming them clearly makes my code much easier to maintain over time.
I hope you found this tutorial useful! Following these methods will help you write cleaner and more efficient React components.
You may also like to read:
- How to Use Await in React Components
- How to Create a Protected Route in React
- How to Use a For Loop in React Functional Components
- How to Convert React Component to TypeScript

I am Bijay Kumar, a Microsoft MVP in SharePoint. Apart from SharePoint, I started working on Python, Machine learning, and artificial intelligence for the last 5 years. During this time I got expertise in various Python libraries also like Tkinter, Pandas, NumPy, Turtle, Django, Matplotlib, Tensorflow, Scipy, Scikit-Learn, etc… for various clients in the United States, Canada, the United Kingdom, Australia, New Zealand, etc. Check out my profile.