To scroll to an element on click in React:
- Set a ref on the target element.
- Set a click listener on the element meant to cause the scroll on click.
- In the click event listener, call the
scrollIntoView()
method on theref
object.
import { useRef } from 'react';
export default function App() {
const ref = useRef(null);
const handleClick = () => {
ref.current?.scrollIntoView({ behavior: 'smooth' });
};
return (
<div>
<button onClick={handleClick}>Scroll to element</button>
<div style={{ height: '150rem' }} />
<div ref={ref} style={{ backgroundColor: 'lightblue' }}>
Coding Beauty
</div>
<div style={{ height: '150rem' }} />
</div>
);
}
Scrolling to an element on click improves user experience by allowing for quick navigation, especially for long lists or pages with multiple sections. It can also add a touch of interactivity to navigation through smooth-scrolling animations.
We create a ref object with the useRef
hook and assign it to the ref
prop of the target div
element. Doing this sets the current
property of the ref object to the DOM object that represents the element.
useRef
returns a mutable object that maintains its value when a component updates. Also, modifying the value of this object’s current
property doesn’t cause a re-render. This is unlike the setState
update function return from the useState
hook.
We use the onClick
on the button to a click listener to it. So, this listener will be called when the user clicks the button.
const handleClick = () => {
ref.current?.scrollIntoView({ behavior: 'smooth' });
};
In the handleClick
listener, we call the scrollIntoView()
method on the ref of the target div
element to scroll down to it and display it to the user.
We set the behaviour
option to smooth
to make the element scroll into view in an animated way, instead of jumping straight to the element in the next frame – auto
. auto
is the default.
Scroll to dynamic element on click in React
We can do something similar to scroll to a dynamically created element in React:
import { useRef, useState } from 'react';
const allFruits = [
'Apples',
'Bananas',
'Oranges',
'Grapes',
'Strawberries',
'Blueberries',
'Pineapples',
'Mangoes',
'Kiwis',
'Watermelons',
];
export default function App() {
const ref = useRef(null);
const [fruits, setFruits] = useState([...allFruits.slice(0, 3)]);
const addFruit = () => {
setFruits((prevFruits) => [...prevFruits, allFruits[prevFruits.length]]);
};
const scrollToLastFruit = () => {
const lastChildElement = ref.current?.lastElementChild;
lastChildElement?.scrollIntoView({ behavior: 'smooth' });
};
return (
<div>
<div
style={{
position: 'fixed',
backgroundColor: 'white',
bottom: 0,
marginBottom: 10,
}}
>
<button onClick={addFruit}>Add fruit</button>
<button onClick={scrollToLastFruit} style={{ marginLeft: '8px' }}>
Scroll to last
</button>
</div>
<div style={{ height: '5rem' }} />
<div ref={ref}>
{fruits.map((fruit) => (
<h2 key={fruit}>{fruit}</h2>
))}
</div>
<div style={{ height: '150rem' }} />
</div>
);
}
Here we have a list of fruits displayed. The Add fruit
button dynamically adds an item to the fruit list on click. Then the Scroll to last
button scrolls to this item on click, as it’s the last in the list.
Like before, we use the onClick
prop to set a click event listener on the button.
This time we set the ref on the list instead of the items since the items are created dynamically, and the last item will not be constant. Doing this sets the current
property of the ref object to the DOM object that represents the list element.
In this listener, we use the lastElementChild
property of the list element to get its last item element. Then we call scrollIntoView()
on this last item to scroll down to it.
const scrollToLastFruit = () => {
const lastChildElement = ref.current?.lastElementChild;
lastChildElement?.scrollIntoView({ behavior: 'smooth' });
};
Create and scroll to dynamic element on click
We can also combine the element creation and scroll as a single action caused by a button click.
To do this, we’ll add a useEffect
hook to call scrollToLastFruit()
whenever the fruits
state array changes.
const addFruit = () => {
setFruits((prevFruits) => [...prevFruits, allFruits[prevFruits.length]]);
};
const scrollToLastFruit = () => {
const lastChildElement = ref.current?.lastElementChild;
lastChildElement?.scrollIntoView({ behavior: 'smooth' });
};
// 👇 Call `scrollToLastFruit()` when `fruits` changes.
useEffect(() => {
scrollToLastFruit();
}, [fruits]);
Here’s the full code for reference:
import { useEffect, useRef, useState } from 'react';
const allFruits = [
'Apples',
'Bananas',
'Oranges',
'Grapes',
'Strawberries',
'Blueberries',
'Pineapples',
'Mangoes',
'Kiwis',
'Watermelons',
];
export default function App() {
const ref = useRef(null);
const [fruits, setFruits] = useState([...allFruits.slice(0, 3)]);
const addFruit = () => {
setFruits((prevFruits) => [...prevFruits, allFruits[prevFruits.length]]);
};
const scrollToLastFruit = () => {
const lastChildElement = ref.current?.lastElementChild;
lastChildElement?.scrollIntoView({ behavior: 'smooth' });
};
useEffect(() => {
scrollToLastFruit();
}, [fruits]);
return (
<div>
<div
style={{
position: 'fixed',
backgroundColor: 'white',
bottom: 0,
marginBottom: 10,
}}
>
<button onClick={addFruit}>Add fruit and scroll</button>
</div>
<div style={{ height: '5rem' }} />
<div ref={ref}>
{fruits.map((fruit) => (
<h2 key={fruit}>{fruit}</h2>
))}
</div>
<div style={{ height: '150rem' }} />
</div>
);
}
Key takeaways
- To scroll to an element on click in React, set a ref on the target element, set a click listener on the element meant to cause the scroll, and in the click event listener, call the
scrollIntoView()
method on the ref object. - To scroll to a dynamically created element in React, set the ref on the list container and call
scrollIntoView()
on the target item element. - We can combine element creation and scrolling as a single action by using the
useEffect
hook to callscrollToLastFruit()
whenever the state array changes.
Every Crazy Thing JavaScript Does
A captivating guide to the subtle caveats and lesser-known parts of JavaScript.