To pass an argument to an event handler in React, wrap the call in an arrow function inside the JSX. The arrow function runs only when the event fires, so the argument arrives at the right time instead of during render. This keeps the handler from running early while still giving it the data it needs.
Why a direct call fails
A bare call runs too early. Code between JSX braces executes during rendering, so onClick={handleClick("S")} calls the handler while the component renders and passes its return value to React. The symptom is usually a handler that runs once on mount and then never again, because there is no function left for React to call on click.
Wrap the call in an arrow function
The arrow function fixes the timing. It captures the argument and defers the call until the click.
import { useState } from "react";
function SizePicker() {
const [size, setSize] = useState("M");
function handlePick(value) {
setSize(value);
}
return <button onClick={() => handlePick("S")}>Small</button>;
}Clicking Small runs the arrow function, which calls handlePick with the value S. The state changes to S for the next render, so a heading that reads size would update to show the new selection. The arrow captures the value at render time, then hands it to the handler only on click.
Pass an id from a list
The most common case is a list where each button must identify its own item. The arrow function closes over the current item and passes its id. Without that captured value, every button would act on the same data or on nothing at all.
{todos.map((todo) => (
<button key={todo.id} onClick={() => handleRemove(todo.id)}>
{todo.text}
</button>
))}Each button receives its own arrow function with the matching id captured. Clicking the second button removes the second item, not the first, because each row carries the id it was rendered with.
Keep the event object
Sometimes the handler needs both the event and an argument. Forward the event as the first parameter.
<button onClick={(e) => handleRemove(e, todo.id)}>Remove</button>The arrow function receives the event from React and passes it on, so the handler can still call methods like preventDefault on it. This pattern is common when a click must both identify an item and cancel a default action. Some teams prefer bind instead, but the arrow function reads more clearly in JSX for one or two arguments.
Common mistakes
- Writing a bare call, which runs during render.
- Reading the event target when the item id is already in the closure.
- Passing arguments through bind when an arrow function is clearer.
What to learn next
The basics of attaching and naming handlers are covered in the event handling overview. When the argument you need is the typed value, the onChange guide shows how to read it from the event target.
Rune AI
Key Insights
- Wrap handler calls in an arrow function inside JSX.
- A bare call runs during render, before any click.
- Forward the event as the first argument when needed.
- Handlers can read props and state directly through closures.
- Use the item id from a list, not the event target.
Frequently Asked Questions
Why does onClick={handleClick(id)} break?
How do I pass an argument and still read the event?
Can I use bind instead of an arrow function?
Conclusion
Wrap the call in an arrow function to pass arguments at click time instead of render time. The handler reads state through its closure, and the event object stays available when you need it.
More in this topic
How to Build a Dropdown Menu in React
Build a React dropdown menu with the ARIA menu button pattern. Handle open and close, keyboard arrows, and clicks outside the menu.
How to Animate React Components with Motion
Animate React components with the Motion library. Set up motion, add enter, hover, and exit animations, and respect reduced motion.
Headless UI Components Explained: Logic Without Locked Styling
Understand headless UI components and how libraries like Radix give you unstyled, accessible behavior that you style yourself.