I tried to implement page transition animations in Next.js 14 (app router) using Framer Motion.
I expected exit and entrance animations to occur when transitioning from page to page.
However, only the entrance animations worked as expected, there were no exit animations.
I attempted to address this by wrapping my page in a HOC as suggested here: /a/77715364/21373653
This enabled the exit animations, but has led to another issue. I now get an error message (below), and the pages sometimes don’t render after a transition – usually the first few transitions after a refresh will come up blank and after that it works as intended. I’m looking for an alternate solution that will fix this issue, or if there is something in my implementation that needs to be fixed. I’m new to Next.js and fairly inexperienced at coding overall so I’m out of my depth with this issue.
app>layout.tsx:
import PageAnimatePresence from './components/HOC/PageAnimatePresense';
import Parallax from './components/Parallax';
import Preloader from './components/Preloader';
import { Theme } from '@radix-ui/themes';
...
export default function RootLayout({
children,
}: Readonly<{
children: React.ReactNode;
}>) {
return (
<html lang="en">
<body className={roboto.variable}>
<Theme
hasBackground={false}
panelBackground='translucent'
>
<Parallax>
<PageAnimatePresence>
{children}
</PageAnimatePresence>
</Parallax>
<Preloader />
</Theme>
</body>
</html>
);
}
app>template.js:
'use client';
import { motion } from 'framer-motion';
import { ReactNode } from 'react';
interface Props {
children: ReactNode
}
const variants = {
hidden: { opacity: 0, x: "100%", y: 0 },
enter: { opacity: 1, x: 0, y: 0 },
exit: { opacity: 0, x:"-100%", y:0 }
}
export default function Template({ children }: Props) {
return (
<motion.main
key="LandingPage"
variants={variants}
initial="hidden"
exit="exit"
animate="enter"
transition={{ type: 'linear', duration: 0.2 }}
>
{children}
</motion.main>
)
}
app>components>HOC>FrozenRoute.tsx:
'use client'
import { LayoutRouterContext } from 'next/dist/shared/lib/app-router-context.shared-runtime'
import { ReactNode } from 'react'
import {
useContext,
useRef
} from 'react'
interface Props {
children: ReactNode
}
const FrozenRoute = ({ children }: Props) => {
const context = useContext(LayoutRouterContext)
const frozen = useRef(context).current
return <LayoutRouterContext.Provider value={frozen}>{children}</LayoutRouterContext.Provider>
}
export default FrozenRoute
app>components>HOC>PageAnimatePresence.tsx:
'use client'
import { ReactNode } from 'react'
import { usePathname } from 'next/navigation'
import {
AnimatePresence,
motion
} from 'framer-motion'
import FrozenRoute from './FrozenRoute'
interface Props {
children: ReactNode
}
const PageAnimatePresence = ({ children }: Props) => {
const pathname = usePathname()
return (
<AnimatePresence mode="wait">
{/**
* We use `motion.div` as the first child of `<AnimatePresence />` Component so we can specify page animations at the page level.
* The `motion.div` Component gets re-evaluated when the `key` prop updates, triggering the animation's lifecycles.
* During this re-evaluation, the `<FrozenRoute />` Component also gets updated with the new route components.
*/}
<motion.div key={pathname}>
<FrozenRoute>{children}</FrozenRoute>
</motion.div>
</AnimatePresence>
)
}
export default PageAnimatePresence
error message:
Warning: Detected multiple renderers concurrently rendering the same context provider. This is currently unsupported.
at FrozenRoute (webpack-internal:///(ssr)/./app/components/HOC/FrozenRoute.tsx:14:24)
at div
at MotionComponent (webpack-internal:///(ssr)/./node_modules/framer-motion/dist/es/motion/index.mjs:49:65)
at PresenceChild (webpack-internal:///(ssr)/./node_modules/framer-motion/dist/es/components/AnimatePresence/PresenceChild.mjs:15:26)
at AnimatePresence (webpack-internal:///(ssr)/./node_modules/framer-motion/dist/es/components/AnimatePresence/index.mjs:72:28)
at PageAnimatePresence (webpack-internal:///(ssr)/./app/components/HOC/PageAnimatePresense.tsx:15:32)
at Lazy
at div
at div
at Parallax (webpack-internal:///(ssr)/./app/components/Parallax.tsx:19:21)
at Lazy
at div
at eval (webpack-internal:///(ssr)/./node_modules/@radix-ui/themes/dist/esm/components/theme.js:83:50)
at eval (webpack-internal:///(ssr)/./node_modules/@radix-ui/themes/dist/esm/components/theme.js:39:25)
at $f631663db3294ace$export$c760c09fdd558351 (webpack-internal:///(ssr)/./node_modules/@radix-ui/react-direction/dist/index.mjs:15:18)
at Provider (webpack-internal:///(ssr)/./node_modules/@radix-ui/react-context/dist/index.mjs:47:28)
at $a093c7e1ec25a057$export$f78649fb9ca566b8 (webpack-internal:///(ssr)/./node_modules/@radix-ui/react-tooltip/dist/index.mjs:72:29)
at eval (webpack-internal:///(ssr)/./node_modules/@radix-ui/themes/dist/esm/components/theme.js:26:114)
at Lazy
at body
at html
at RedirectErrorBoundary (webpack-internal:///(ssr)/./node_modules/next/dist/client/components/redirect-boundary.js:73:9)
at RedirectBoundary (webpack-internal:///(ssr)/./node_modules/next/dist/client/components/redirect-boundary.js:81:11)
at ReactDevOverlay (webpack-internal:///(ssr)/./node_modules/next/dist/client/components/react-dev-overlay/app/ReactDevOverlay.js:87:9)
at HotReload (webpack-internal:///(ssr)/./node_modules/next/dist/client/components/react-dev-overlay/app/hot-reloader-client.js:322:11)
at Router (webpack-internal:///(ssr)/./node_modules/next/dist/client/components/app-router.js:202:11)
at ErrorBoundaryHandler (webpack-internal:///(ssr)/./node_modules/next/dist/client/components/error-boundary.js:112:9)
at ErrorBoundary (webpack-internal:///(ssr)/./node_modules/next/dist/client/components/error-boundary.js:158:11)
at AppRouter (webpack-internal:///(ssr)/./node_modules/next/dist/client/components/app-router.js:556:13)
at Lazy
at r1 (C:Userswood3OneDriveDocumentsPortfolio-Nextportfolionode_modulesnextdistcompilednext-serverapp-page.runtime.dev.js:39:18701)
at r1 (C:Userswood3OneDriveDocumentsPortfolio-Nextportfolionode_modulesnextdistcompilednext-serverapp-page.runtime.dev.js:39:18701)
at ServerInsertedHTMLProvider (C:Userswood3OneDriveDocumentsPortfolio-Nextportfolionode_modulesnextdistcompilednext-serverapp-page.runtime.dev.js:39:24131)
Wood3001 is a new contributor to this site. Take care in asking for clarification, commenting, and answering.
Check out our Code of Conduct.