Reputation: 131
I want to create transitions between my pages with Framer Motion in my Next.js 14 project, which is why I'm using the Page router.
I've created a layout to apply my transition effects to all my pages:
MainLayout.tsx
export default function MainLayout({
children,
}: {
children: React.ReactNode;
}) {
const animation = (variants) => ({
initial: 'initial',
animate: 'animate',
exit: 'exit',
variants,
});
const pagesVariants = {
initial: {
opacity: 0,
},
animate: {
opacity: 1,
transition: {
duration: 6, // I put a high value intentionally to see the animation
},
},
exit: {
opacity: 1,
},
};
return (
<>
<Header />
<AnimatePresence mode="wait">
<motion.div
id="main"
className="flex grow flex-col"
{...animation(pagesVariants)}
>
{children}
</motion.div>
</AnimatePresence>
<Footer />
</>
);
}
_app.tsx
export default function App({ Component, pageProps, router }: AppProps) {
return (
<div
id="app"
className={`${amarante.variable} ${metal.variable} ${viaodaLibre.variable} ${philosopher.variable} ${styles.app} font-text`}
>
<MainLayout>
<Component {...pageProps} key={router.route} />
</MainLayout>
</div>
);
}
Unfortunately, the fade effect only works when I refresh the page. When I change the page via the Link component, the effect doesn't work. No matter where I place my AnimatePresence component (in my layout file or in my app file), it makes no difference. I've also added 'use client' to the top of each of my pages and layouts, but that doesn't change anything.
Upvotes: 0
Views: 558
Reputation: 131
The problem was due to misuse of my MainLayout component. I thought this would be enough to make the animation happen to all my children components, but no.
For a start, the code in the _app.tsx file shouldn't change that much. I may have to wrap my header and footer, but here's the code:
export default function App({ Component, pageProps, router }: AppProps) {
return (
<div
id="app"
className={`${amarante.variable} ${metal.variable} ${viaodaLibre.variable} ${philosopher.variable} ${styles.app} font-text`}
>
<Header />
<AnimatePresence mode="wait">
<Component key={router.route} {...pageProps} />
</AnimatePresence>
<Footer />
</div>
);
}
I've renamed my MainLayout component to AnimationProvider to make it more meaningful. The 'animation' and 'pageVariants' variables remain unchanged. Here's what the new component returns:
return (
<motion.main id={pageName} {...animation(pageVariants)}>
{children}
</motion.main>
);
Next, I need to wrap each page with my AnimationProvider component:
export default function Contact() {
return (
<AnimationProvider pageName="contact">
<h1>Contact</h1>
</AnimationProvider>
);
}
Upvotes: 0