-
Hey there! I was wondering, how to go about animating route transitions in The docs mention that using the Does anyone have any ideas? Thanks! |
Beta Was this translation helpful? Give feedback.
Replies: 36 comments 44 replies
-
I tried using Template and Layout in a way that it should work based on Next documentation but I still can't get exit animations to work. Have you tried implementing something? |
Beta Was this translation helpful? Give feedback.
-
Hi, Sorry for posting a YT video, but they pretty much answer your question in various ways: https://www.youtube.com/watch?v=GksAsutVXOA You'd be most interest into 6:30 All you ought to do is wrap each page with a motion.div, but you ought to do this in a separate file with |
Beta Was this translation helpful? Give feedback.
-
I'm also running in to this issue. I suspect that the |
Beta Was this translation helpful? Give feedback.
-
This is a bug. I don’t think you can achieve exit animation in appdir. |
Beta Was this translation helpful? Give feedback.
-
For reference, there's a thread in the framer/motion that discusses this topic too. There are no answers there, but some other references to the video tutorial linked elsewhere in this thread, with commenters on the video also struggling to get exit animations to work. Goes without saying that I'm interested in this too. I've tried every combination I can think of without success. I suspect it'll need to be fixed by the Next.js team. |
Beta Was this translation helpful? Give feedback.
-
As I also can't get this to work at all, for my fellow issue-googleers, here's my "fun and not at all a hack" way of achieving this: Step 1: Replace all your The rest: 'use client'
import { usePathname, useRouter } from 'next/navigation'
import { useEffect, useState } from 'react'
import Template from './template'
export default function ClientRootLayout({
children
}: {
children: React.ReactNode
}) {
const router = useRouter()
const path = usePathname()
const [loading, setLoading] = useState(false)
// To stop the first page transitioning in, unless you want that, in which case remove this bit of logic
const [isInitialLoading, setIsInitialLoading] = useState(true)
useEffect(() => {
// Bind react router navigation event to all a tags
const onClick = (e: any) => {
const target = e.target as HTMLElement
var foundTarget = target
if (
target.tagName.toLowerCase() !== 'a' &&
target.tagName.toLowerCase() !== 'button'
) {
const closestAnchor = target.closest('a')
if (closestAnchor) {
foundTarget = closestAnchor
}
}
const lcTagName = foundTarget.tagName.toLowerCase()
if (lcTagName === 'a' || lcTagName === 'button') {
const href = foundTarget.getAttribute('href')
if (href && href.startsWith('/')) {
e.preventDefault()
if (href !== path) {
setLoading(true)
}
router.push(href)
}
}
}
window.addEventListener('click', onClick)
return () => window.removeEventListener('click', onClick)
}, [router, path])
useEffect(() => {
window.scrollTo(0, 0)
setLoading(false)
setIsInitialLoading(false)
}, [path])
return (
<Template loading={!isInitialLoading && loading}>{children}</Template>
)
}
'use client'
import PageLoadingAnimation from '@/components/PageLoadingAnimation'
import { motion } from 'framer-motion'
import { useEffect, useRef, useState } from 'react'
export default function Template({
children,
loading,
}: {
children: React.ReactNode
loading: boolean
}) {
return (
<>
<motion.div
style={{ height: '100%' }}
initial={{ x: 300, opacity: 0 }}
animate={{
x: loading ? 300 : 0,
opacity: loading ? 0 : 1,
}}
transition={{
type: 'spring',
duration: 0.5,
}}
>
{children}
</motion.div>
</>
)
} This is split across a couple of files to aid me copy-pasting it out of my project, but it can all live inside your client layout component. The key is to manage the loading state yourself by intercepting link clicks and pushing them to the router, and send that loading state through to whatever is going to do your transitions. This example is a simple fade and slide. |
Beta Was this translation helpful? Give feedback.
-
https://greensock.com/forums/topic/29470-gsap-page-transitions-in-nextjs/?do=findComment&comment=190422&_rid=153488 |
Beta Was this translation helpful? Give feedback.
-
I also encountered this issue and have found a (hacky) but decently easy to implement solution: export const LayoutTransition = ({
children,
}: {
children: React.ReactNode;
}) => {
const pathname = usePathname();
const lastPageRef = useRef<HTMLCollection | null>(null);
const currentPageRef = useRef<HTMLDivElement>(null);
const exitAnimationDivRef = useRef<HTMLDivElement>(null);
useLayoutEffect(() => {
if (!currentPageRef.current) return;
if (!lastPageRef.current)
lastPageRef.current = currentPageRef.current.children;
exitAnimationDivRef.current?.appendChild(
lastPageRef.current![0].cloneNode(true)
);
lastPageRef.current = currentPageRef.current.children;
}, [pathname]);
return (
<AnimatePresence initial={false}>
<div>
<motion.div
key={pathname + "exit-animation"}
style={{
position: "absolute",
}}
initial={{ x: 0 }}
animate={{
x: "-100%",
opacity: 0,
}}
transition={{
type: "linear",
duration: 0.2,
}}
>
<div ref={exitAnimationDivRef} />
</motion.div>
<motion.div
key={pathname}
initial={{ x: "100%" }}
animate={{ x: 0 }}
transition={{ type: "linear", duration: 0.2 }}
>
<div ref={currentPageRef}>{children}</div>
</motion.div>
</div>
</AnimatePresence>
);
}; This will clone the last rendered page into the dom and then animate it out when the page changes. Works quite well for my use case. Edit: Forgot to mention, this wrapper is to be used in a layout.tsx file |
Beta Was this translation helpful? Give feedback.
-
Have started a discussion around the place in the code this needs to be done, as well as a proof-of-concept PR with working route template transitions using |
Beta Was this translation helpful? Give feedback.
-
Based on what you mentioned, it seems like the docs are suggesting using the template.js file instead of layout.js for handling route transitions. This probably means that the template.js file contains the necessary code or hooks to implement animations when navigating between different routes in your app directory. |
Beta Was this translation helpful? Give feedback.
-
My implementation is quite similar to previous ones but works like a provider that can be used directly in _app.tsx
|
Beta Was this translation helpful? Give feedback.
-
@leerob could we get your input on this, please? 😅 ❤️ |
Beta Was this translation helpful? Give feedback.
-
How is this still an issue a year later, never had these issues in gatsby... |
Beta Was this translation helpful? Give feedback.
-
If anybody is stuck trying to create page transitions with GSAP where both pages intersect, here's how I did it using the 'frozen route' solution mentioned earlier. Working live preview : https://transition-next.vercel.app/ EDIT: I got rid of the 'frozen route' and just cloned the content to a new div like in the solution above. 😕
It works fine for my use case, but I'm not sure how to accomplish this in a different way. 🫤 |
Beta Was this translation helpful? Give feedback.
-
No credit to me but to the authour on this stackblitz: https://stackblitz.com/edit/nextjs-framer-motion-template-p2cr5m?file=app%2FPageTransitionEffect.tsx. Works like a charm for me. But I added inital={false} to AnimatePresence to ignore inital animation. |
Beta Was this translation helpful? Give feedback.
-
It's 2024.🥲 Cc: @leerob |
Beta Was this translation helpful? Give feedback.
-
People should really be told this prior to learning the App router :/ |
Beta Was this translation helpful? Give feedback.
-
Even with this bug/issue in place, I don't want to use page router at all for newer projects. Still looking forward to an update with fixes. I have large projects which requires these basic features. |
Beta Was this translation helpful? Give feedback.
-
It's funny how nobody from the Next.js team is even the slightest bit involved in this discussion. As a newcomer, you immediately feel the cozy warmth of a user-oriented framework. |
Beta Was this translation helpful? Give feedback.
-
👋 Update here:
|
Beta Was this translation helpful? Give feedback.
-
Wow, nice to see this |
Beta Was this translation helpful? Give feedback.
-
Yeah this is not a Framer Motion problem. Exit animations cannot work in the App Router with Next's render stack. This article is a nice read about the problem. This issue (specifically this comment) are also worth checking out. Wasn't a problem in Pages Router and in other routers like Tanstack Router or React Router. The current workaround is hacking on the internal Would be awesome to have a transitions API like the one in Nuxt or other animation-oriented routers, like Taxi or Swup. View Transitions are awesome but for design-oriented work you want to serve page transitions to the majority of your traffic. You don't really see View Transitions on sites like the ones showcased here |
Beta Was this translation helpful? Give feedback.
-
NextJS just need to sort this out tbh, I’m close to moving to a different framework entirely due to this issue. It’s a joke now
|
Beta Was this translation helpful? Give feedback.
-
Completely agree Rijk!On 7 Aug 2024, at 13:48, Rijk van Wel ***@***.***> wrote:
Bit disappointing after all this time to see you simply passing the hot potato to Framer Motion. Obviously they are not planning a fix because it’s not a bug; the thread you linked was just people using it wrong. The real issue as has been documented thoroughly by others is that rather than simply rendering the <Page> in the Layout’s {children} spot, Next wraps it in all kinds of providers and components behind the scenes, which breaks AnimatePresence’s ability to detect changes in its children. This wrapping is undocumented, unexpected and another one of the ways the framework actively gets in your way, which is a big issue in the latest versions of Next (been using since v9 btw).
It seems to me this should be easily fixable, by either (in my order of preference):
Wrapping the internal page provider tree in an element with a unique key (this would allow AnimatePresence to detect change)
Giving us a usePathname hook that runs in sync with the router so we can wrap the children ourselves (currently that doesn’t work well)
Provide access to the router’s internal transition pending value (e.g. a useIsNavigating hook)
Giving us access to router events like we had in the pages router
Lastly, I stand by my previous comment, that page transitions are table stakes functionality especially for SPA frameworks, and should not require complex user land implementations.
—Reply to this email directly, view it on GitHub, or unsubscribe.You are receiving this because you commented.Message ID: ***@***.***>
|
Beta Was this translation helpful? Give feedback.
-
This issue was opened 2 years ago. It's pretty astonishing that the biggest react framework hasn't figured out how to properly do exit route transitions. Especially when this was a non-issue in the pages router. |
Beta Was this translation helpful? Give feedback.
-
If bundle size is not an issue you can do something like this in layout, you can import your pages from the file path if you don't want to define them shown in this example, if you need server calls in layout, create an App.js file with similar logic (still import pages here) there. Your pretty much opting in to CSR here at this point. PS: Don't ask about apple-mobile-web-app-capable. On my iPhone 14 on the latest update requires if for Splash Screens to work. Deprecated? My test say... NO.
|
Beta Was this translation helpful? Give feedback.
-
Any update? Is this on the roadmap for an upcoming Next.js release? |
Beta Was this translation helpful? Give feedback.
-
Hey everyone, I wanted to share one solution that might be relevant for you all. Here was my use case:
I can definitely empathize with this being confusing, and apologize we haven't done enough to create examples or show how this could be possible. For example, trying to animate across routes using The solution I landed on is this:
This isn't perfect – for example, if you manually swipe back in iOS Safari. The most ideal solution here is View Transitions, which you can use https://next-view-transitions.vercel.app for in some cases. However, I couldn't get it working perfectly for the animation I was going for in this case, and also there isn't Firefox support yet for View Transitions. I do believe this is the best solution in the future though. CleanShot.2024-12-29.at.14.56.21.mp4Hope this helps! 🙏 |
Beta Was this translation helpful? Give feedback.
-
For people still looking for a solution, check out https://github.com/ismamz/next-transition-router. |
Beta Was this translation helpful? Give feedback.
Hey everyone, I wanted to share one solution that might be relevant for you all. Here was my use case:
/
to an individual product page/p/[slug]
needs to correctly start on the detail state (no animation)I can definitely empathize with this being confusing, and apologize we haven't done enough to create examples or show how this could be possible. For example, trying to animate across routes using
framer-motion
(nowmotion
) using the defaultpage
files in the App Router …