Best Practice to Show Loading Indicator on Static Pages in Next.js App Dir
Unanswered
Snowshoe posted this in #help-forum
SnowshoeOP
In a production-ready Next.js 15 app (using the App Router), how do you handle showing a loading indicator for static pages that don’t use async/data fetching at the top level?
Right now, my loading.tsx doesn't get triggered because the page isn't doing any await/fetch() calls. As a result, users briefly see a blank page before the content appears.
Curious what others are doing in production to make sure static or fast-rendering pages still show a loader (even briefly) to avoid a blank screen flash. Do you force suspense? Use a shell layout? Open to patterns or suggestions. ( I would not like to wrap the whole application in a template)
Right now, my loading.tsx doesn't get triggered because the page isn't doing any await/fetch() calls. As a result, users briefly see a blank page before the content appears.
Curious what others are doing in production to make sure static or fast-rendering pages still show a loader (even briefly) to avoid a blank screen flash. Do you force suspense? Use a shell layout? Open to patterns or suggestions. ( I would not like to wrap the whole application in a template)
15 Replies
Silver Fox
I don't think there's a good way to add a loading spinner without slowing down the overall experience. Once you build and deploy the app, the static parts should be cached at a CDN, which should speed things up.
As far as I know I've never seen a webpage that has a brief white flash on the first load, just because of how browsers work.
SnowshoeOP
I am using a DotLottieReact file there . Maybe that caused the delay while it loads
Silver Fox
I see... I don't have much experience with DotLottie React but from a quick search it doesn't look like there's a common practice for delaying its loading. Claude recommends manually listening for
requestIdleCallback
([developer.mozilla.org/.../requestIdleCallback](https://developer.mozilla.org/en-US/docs/Web/API/Window/requestIdleCallback) — it will delay its mount until everything else has loaded) like this:function IdleRenderer({ children }) {
const [shouldRender, setShouldRender] = useState(false);
useEffect(() => {
if ('requestIdleCallback' in window) {
const handle = requestIdleCallback(() => setShouldRender(true));
return () => cancelIdleCallback(handle);
} else {
// Fallback for browsers without requestIdleCallback
const timer = setTimeout(() => setShouldRender(true), 200);
return () => clearTimeout(timer);
}
}, []);
return shouldRender ? children : null;
}
// Usage
<div>
<h1>Important content</h1>
<p>More important content</p>
<IdleRenderer>
<ExpensiveComponent />
</IdleRenderer>
</div>
SnowshoeOP
Could it be an issue I use redirect to navigate to the page?
Brown bear
Yes, redirect should not be used. Use <Link/>
(or useRouter)
SnowshoeOP
Yeah but i wanted to navigate user to an other page when XYZ... User basically navigation is not bcs of a user click
and useRouter would make it client, right?
Brown bear
The component, not the page itself
SnowshoeOP
Yeah but i wanted to redirect before rendering the page from page X to page Y and show loading on page Y while static page loads
Brown bear
Is redirect() happening in a server component or in a server action?
SnowshoeOP
Server component
Brown bear
Try moving it to a server action if you can, see if that fixes it. There actually seems to be a bug with it
SnowshoeOP
Or maybe I could render a client component that automatically redirects to the other page