[BUG] Opacity animation is delayed on page load/reload with Next.js
1. Describe the bug
Opacity animation is delayed on page load/reload with Next.js, only on Chrome and when deployed on Vercel.
On a typical fade in animation (opacity 0→1, Y translate), the opacity animation delay causes the translate animation not to be visible.
2. IMPORTANT: Provide a CodeSandbox reproduction of the bug
The bug is only reproduced when deployed on Vercel. The bug does not happen in CodeSandbox, but it is also provided for comparison.
3. Steps to reproduce
Steps to reproduce the behavior:
- Using Chrome, navigate to the app deployed on Vercel: https://test-next-framer.vercel.app/
- See animation on page load.
- Reload the page
- See animation on page reload. The Y-translate animation of the blue square is not visible because -apparently- the opacity animation is delayed.
I see the issue also occasionally on first page load, but not always.
4. Expected behavior
The blue square should animate it's opacity 0 → 1 at the same time as it Y-translates from the bottom container to the top
5. Video or screenshots
✅ Correct behavior (Vercel, Safari):
https://github.com/framer/motion/assets/1535759/6443da59-5005-4d80-ae70-da3cb8e9ee27
✅ Correct behavior (CodeSandbox, Chrome):
https://github.com/framer/motion/assets/1535759/14f634d4-7ef2-4e44-b5f7-943108c316a6
❌ Incorrect behavior (Vercel, Chrome):
https://github.com/framer/motion/assets/1535759/eb571955-54a9-4cab-a2a6-f9c0b20cc03e
6. Environment details
- macOS Ventura 13.5
- Google Chrome 117.0.5938.149
- Next.js 13.5.4
- Framer Motion 10.16.4
- Deployed on Vercel
I suspect that as this a contentless page, it's falling victim to something like https://bugs.chromium.org/p/chromium/issues/detail?id=1406850#c_ts1673770223
Could you add some text and see if anything changes?
Is there any temporary fix for this? The transition is buggy in production but not in development.
I had the same issue which I just fixed by not propping down server components inside of a client component (which had my motion code)
In your case I would suggest to convert your page.tsx into a normal server component. Abstract the motion code into its own file, with a children prop so you can pass down your client components
Now you have your motion component which you can use to nest your client components. For some reason if you nest server components, they won't have that animation. It only works with client components.
My example
fade-in.tsx
import * as motion from "motion/react-client";
export default function FadeIn({
children,
}: {
children: React.ReactNode;
}) {
return (
<motion.div
layout
initial={{ opacity: 0 }}
animate={{ opacity: 1 }}
>
{children}
</motion.div>
);
}
page.tsx
import { Suspense } from "react";
import FadeIn from "@/components/admin/animations/fade-in";
import Loader from "@/components/admin/animations/loader";
export default function Page() {
return (
<Suspense fallback={<Loader />}>
<FadeIn>
{/* client components. */}
</FadeIn>
</Suspense>
);
}