Here's a small thing that bugs me: you click a button, wait for something to load, and then... nothing happens on screen. The content loaded, but it's hiding below the fold. You have to notice the scrollbar changed and scroll down yourself.
We can do better. And it takes like 3 lines of code.
Meet scrollIntoView()
Every DOM element has this method built in. Call it, and the browser scrolls until that element is visible.
element.scrollIntoView({ behavior: "smooth" });
That's literally it. Smooth animated scroll, right to the element. Done.
A Real Example: Auto-Scrolling to AI-Generated Content
I was building a recipe generator app in React. The user adds ingredients, clicks "Get a Recipe," and an AI returns a recipe. The problem? On smaller screens, the recipe loads completely off-screen. No visual feedback at all.
Here's how I fixed it.
1. Create a ref
In React, we use useRef to grab DOM elements instead of document.getElementById() — it plays nicer with reusable components since you won't end up with duplicate IDs.
import { useRef, useEffect, useState } from "react";
function Main() {
const [recipe, setRecipe] = useState("");
const recipeSectionRef = useRef(null);
2. Attach it to the element you want to scroll to
return (
<div>
{/* ingredients form up here */}
<div ref={recipeSectionRef}>
{recipe ? <Recipe data={recipe} /> : <p>Ready for a recipe?</p>}
</div>
</div>
);
}
3. Scroll when the recipe loads
useEffect(() => {
if (recipe && recipeSectionRef.current) {
recipeSectionRef.current.scrollIntoView({ behavior: "smooth" });
}
}, [recipe]);
That's the whole thing. When recipe updates, the page gently scrolls down so the user can see it. No confusion, no hunting around.
Why useRef instead of an ID?
Totally fair question. getElementById would work! But React components are meant to be reusable. If your component ever renders twice on a page, you'd have duplicate IDs — which is invalid HTML and leads to weird bugs. Refs are scoped to the component instance, so they're always safe.
Quick note on refs
If you're new to useRef, here's the two-sentence version: refs are like state, but changing them doesn't trigger a re-render. They're an object with a .current property — when you attach a ref to a DOM element, .current becomes that actual DOM node.
const myRef = useRef(null);
// after render: myRef.current === the actual <div> element
Where else is this useful?
Anywhere new content appears and you want to guide the user's eyes to it — new chat messages, form validation errors, search results loading in, accordion sections opening. It's a tiny detail, but it's the kind of thing that makes an app feel thoughtful.
Sometimes the best UX improvements are the smallest ones. Three lines of code, and your users never have to wonder "did that work?" again.
Credits: Scrimba
Top comments (1)
On Scrimba,
recipeSection.current.scrollIntoView({behavior: "smooth"})didn't work smoothly since there's a bug in the latest Chrome when using iframes, and Scrimba shows the mini browser using an iframe. So a workaround was to use: