Replies: 20 comments 23 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.
-
Just found this useful tutorial, it works perfectly on my side |
Beta Was this translation helpful? Give feedback.
-
I think I found a pretty elegant way to do this using transitions. Whipped up a quick demo here: https://app-router-transitions.vercel.app I leverage the behavior that during a (route) transition, the current page stays on screen until the new route is ready. We can combine that with an I've made a simple How it works is essentially as follows:
|
Beta Was this translation helpful? Give feedback.
-
Are you kidding me? This needs to be highlighted in red in the app router docs as a serious limitation. |
Beta Was this translation helpful? Give feedback.
-
it's a real shame this limitation exists and something that should definitely be supported by the app router by now |
Beta Was this translation helpful? Give feedback.
-
Here is a demo that I have been able to make from the code that @whatisjery shared a few months ago. Demo: https://nextjs-app-directory-page-transitions.vercel.app/ It keeps the scroll when starting the page transition and restores it when navigating backwards, use sessionstorage for that, I don't know if it's the best way but for me it works great. Repo: https://github.com/diarpu/nextjs-app-directory-page-transitions |
Beta Was this translation helpful? Give feedback.
-
Hey there!
I was wondering, how to go about animating route transitions in
app
directory?The docs mention that using the
template.js
file (instead oflayout.js
) is required but I'd like to learn more!Does anyone have any ideas?
Thanks!
Beta Was this translation helpful? Give feedback.
All reactions