React Mouse Events
Table of Contents + −
In the last lesson you learned about React Keyboard Events, so you can already react when someone types. Now let’s handle the mouse, so you can build hover effects, tooltips, and things that follow the pointer, not just clicks.
🤔 Why do we need mouse events?
Here’s the pain. A plain click handler only fires on one action. Real interfaces need to respond to much more.
- A button should change color when the pointer moves over it, before any click happens.
- A tooltip should appear when you hover, then disappear when you move away.
- A drawing app needs to know where the pointer is as it moves across the screen.
- Some actions only make sense on a double-click, like opening an item.
So clicks alone are not enough. Mouse events let your component respond to hovering, moving, and double-clicking too, not just a single tap.
🧩 What are mouse events?
Mouse events are the messages React sends your component whenever the pointer does something on an element.
- They work just like
onClick. You add a prop with a function, and React calls it when that thing happens. - Each one is named for the action:
onMouseEnter,onMouseLeave,onMouseMove,onDoubleClick, and more. - React passes your function an event object, usually called
e. It holds details like where the pointer is. - The names use camelCase, so it’s
onMouseEnter, notonmouseenter.
Here is the general shape. You attach a handler to an element, and React runs it when the event fires.
function Box() { const handleEnter = () => { console.log("The pointer came in!"); };
return <div onMouseEnter={handleEnter}>Hover over me</div>;}So whenever the pointer moves onto that div, React calls handleEnter. Every other mouse event follows this same pattern.
🖱️ Hover with onMouseEnter and onMouseLeave
The most common thing you’ll build is a hover effect. For that you need two events working together.
onMouseEnterfires once when the pointer moves onto the element.onMouseLeavefires once when the pointer moves off the element.- Pair them with a piece of state, like
isHovered, to remember whether the pointer is currently on top.
Here we keep an isHovered flag in state. We flip it to true on enter and back to false on leave, then use it to change the background color.
import { useState } from "react";
function HoverBox() { const [isHovered, setIsHovered] = useState(false);
return ( <div onMouseEnter={() => setIsHovered(true)} onMouseLeave={() => setIsHovered(false)} style={{ background: isHovered ? "skyblue" : "lightgray", padding: "20px" }} > {isHovered ? "Thanks for hovering!" : "Hover over me"} </div> );}Let’s walk through what happens.
- The pointer moves onto the box, so
onMouseEnterruns and setsisHoveredtotrue. - State changed, so React re-renders. The background turns sky blue and the text updates.
- The pointer moves off, so
onMouseLeaveruns and setsisHoveredback tofalse. The box returns to gray.
This same pattern is how you show a tooltip on hover. You just render the tooltip when isHovered is true.
onMouseEnter vs onMouseOver
There are also onMouseOver and onMouseOut. They look similar, but they
bubble — they also fire when the pointer moves over child elements inside.
onMouseEnter and onMouseLeave do not bubble, so they fire only for the
element itself. That makes them the cleaner choice for hover effects.
📍 Tracking the pointer with onMouseMove
Sometimes you need the exact spot of the pointer, not just that it’s over the element. That’s what onMouseMove is for.
onMouseMovefires again and again as the pointer moves across the element.- The event object gives you the position.
e.clientXis the horizontal position ande.clientYis the vertical one. - Both are measured in pixels from the top-left corner of the visible page.
Here we read e.clientX and e.clientY on every move and store them in state, so we can show the live position.
import { useState } from "react";
function MouseTracker() { const [pos, setPos] = useState({ x: 0, y: 0 });
const handleMove = (e) => { setPos({ x: e.clientX, y: e.clientY }); };
return ( <div onMouseMove={handleMove} style={{ height: "200px", background: "lightyellow" }}> The pointer is at {pos.x}, {pos.y} </div> );}So as you move inside the yellow area, the numbers update in real time.
- Each move fires
handleMove, which reads the new position and saves it to state. - State changed, so React re-renders and the text shows the latest spot.
clientX vs pageX
e.clientX and e.clientY are measured from the visible window. If you scroll
the page, use e.pageX and e.pageY instead, because those include the scrolled
distance. For most small components, clientX and clientY are what you want.
🖱️🖱️ Reacting to a double-click
A single click and a double-click are different actions, and React keeps them separate. For two quick clicks in a row, you use onDoubleClick.
onDoubleClickfires when the user clicks twice quickly on the same element.- It’s handy for actions like opening an item or starting an edit, while a single click does something lighter.
- You attach it exactly like
onClick, with a function.
Here a single click and a double-click each update a message, so you can feel the difference.
import { useState } from "react";
function ClickDemo() { const [message, setMessage] = useState("Try clicking");
return ( <button onClick={() => setMessage("Single click")} onDoubleClick={() => setMessage("Double click!")} > {message} </button> );}So one click shows “Single click”, and two quick clicks show “Double click!”. The onDoubleClick event is what catches that fast second tap.
⬇️⬆️ A quick word on onMouseDown and onMouseUp
A click is really two smaller steps, and React lets you listen to each one on its own.
onMouseDownfires the moment the mouse button is pressed down.onMouseUpfires the moment the button is released.- A normal
onClickis really a down followed by an up on the same element.
You won’t need these every day, but they have their moments.
- They’re useful for drag-and-drop, where you start an action on
onMouseDownand finish it ononMouseUp. - For most clicks, plain
onClickis still the right tool.
Don't reach for these too early
For ordinary buttons and links, stick with onClick. Only use onMouseDown
and onMouseUp when you genuinely need the press and release as separate
steps, like dragging something. Otherwise you’ll write more code than you need.
🧩 What You’ve Learned
- ✅ Mouse events let your component react to hovering, moving, and double-clicking, not just clicks
- ✅
onMouseEnterandonMouseLeavefire when the pointer moves onto and off an element, perfect for hover effects - ✅ Pair them with a state flag like
isHoveredto change styles or show a tooltip - ✅
onMouseEnterandonMouseLeavedo not bubble, unlikeonMouseOverandonMouseOut - ✅
onMouseMovefires as the pointer moves, ande.clientXande.clientYgive you its position - ✅
onDoubleClickcatches two quick clicks for actions like opening an item - ✅
onMouseDownandonMouseUpsplit a click into press and release, useful for drag-and-drop
Check Your Knowledge
Test what you learned. Pick an answer for each question, then click Check.
- 1
Which two events are best for building a hover effect?
Why: onMouseEnter fires when the pointer moves onto the element and onMouseLeave fires when it moves off. Together they track whether the pointer is currently hovering.
- 2
How do you read the pointer's position inside an onMouseMove handler?
Why: The event object carries e.clientX for the horizontal position and e.clientY for the vertical position, measured in pixels from the top-left of the visible window.
- 3
Why are onMouseEnter and onMouseLeave preferred over onMouseOver and onMouseOut for hover?
Why: onMouseOver and onMouseOut bubble, so they also fire when the pointer moves over child elements inside. onMouseEnter and onMouseLeave do not bubble, which makes hover logic cleaner.
- 4
Which event would you use to run code only when the user clicks twice quickly?
Why: onDoubleClick fires when the user clicks twice quickly on the same element, which is useful for actions like opening an item or starting an edit.
🚀 What’s Next?
Now you can handle hovering, moving, and double-clicking with the mouse. But sometimes an event triggers the browser’s own default behavior that you don’t want, like a form reloading the page. Next you’ll learn how to stop that.