How to prevent UI flickering when using useEffect to change styles on page load
Unanswered
Polar bear posted this in #help-forum
Polar bearOP
I'm implementing a header in my Next.js app where the background color changes based on the scroll position: if
The problem is that if a user refreshes the page while scrolled down, the header would initially be rendered with transparent background even if the initial scrollY is greater than 0 then changed to white. This happens because the color changing logic inside useEffect runs after the component mounts, causing a delay before it checks the scroll position.
How can I avoid this flicker effect and make sure the header displays the correct background color immediately on page load without any delay?
window.scrollY
is 0, the background is transparent. Otherwise, it changes to a solid color. I’m using a useEffect hook to set up the scroll listener and adjust the background color state accordingly.The problem is that if a user refreshes the page while scrolled down, the header would initially be rendered with transparent background even if the initial scrollY is greater than 0 then changed to white. This happens because the color changing logic inside useEffect runs after the component mounts, causing a delay before it checks the scroll position.
How can I avoid this flicker effect and make sure the header displays the correct background color immediately on page load without any delay?
"use client"
export default function Header() {
useEffect(() => {
const handleScrollChange = () => {
const header = document.querySelector("header");
if (window.scrollY > 0) {
header?.classList.remove("bg-transparent");
header?.classList.add("bg-white");
} else {
header?.classList.remove("bg-white");
header?.classList.add("bg-transparent");
}
};
handleScrollChange();
window.addEventListener("scroll", handleScrollChange);
return () => window.removeEventListener("scroll", handleScrollChange);
}, []);
return (
<header className="sticky w-full top-0 z-10">
...
</header>
);
}
22 Replies
Polar bearOP
Here's the demo
@Polar bear can you try
next/dynamic
with ssr: false
when you load your header component?Polar bearOP
like this?
const Header = dynamic(() => import("@/components/header"), {ssr: false})
...
return (
...
<Header />
...
)
yeah, exactly
Polar bearOP
That makes the whole header lazy loaded with even more delay
yeah, ofc. you would need a skeleton UI while it's loading
but once it's loaded you won't have flickering
you know Next.js does SSR and on the server side there is no
window
object => that's causing flickering, right?Polar bearOP
yeah
I tried adding script using next/script to change the style but it doesn't work in next.js
make a skeleton (clone your header assuming window.scrollY = 0) and then use it as a loader
Polar bearOP
doesn't that create the same problem since the background color of skeleton can't be determined on the server?
I don't get it. can you give me a code example?
ah, my bad @Polar bear I couldn't look through your code carefully.
so what you can do is hmm
let me share the codebase
"use client"
import { useEffect, useState } from "react";
export default function Header() {
const [isScrolled, setIsScrolled] = useState(false);
useEffect(() => {
const handleScrollChange = () => {
setIsScrolled(window.scrollY > 0);
};
handleScrollChange(); // Initial check on mount
window.addEventListener("scroll", handleScrollChange);
return () => window.removeEventListener("scroll", handleScrollChange);
}, []);
return (
<header
className={`sticky w-full top-0 z-10 transition-colors ${
isScrolled ? "bg-white" : "bg-transparent"
}`}
>
...
</header>
);
}
try this instead
Polar bearOP
That still causes flickering when refreshing page at window.scrollY > 0 (
isScrolled
should be true) since you assume that the initial isScrolled
is false.sorry man, I have no idea then
Polar bearOP
it's ok. good try🙏
I mean, there won't be a perfect solution for this, if you don't want to long delay (my first approach)
Polar bearOP
I think there's a solution. It's the same problem as doing dark mode without color flickering on page load, like what 'next-themes' does. I just don't understand how to implement