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

Bijay Kumar is an experienced Python and AI professional who enjoys helping developers learn modern technologies through practical tutorials and examples. His expertise includes Python development, Machine Learning, Artificial Intelligence, automation, and data analysis using libraries like Pandas, NumPy, TensorFlow, Matplotlib, SciPy, and Scikit-Learn. At PythonGuides.com, he shares in-depth guides designed for both beginners and experienced developers. More about us.