Reputation: 11
I'm adding page transitions to a Gatsby site I'm working on using the framer-motion library. Currently, it works great on my local setup, but when I push to production, the exit animation no longer fires.
I'm wrapping my pages in the AnimatePresence component using gatsby-browser.js:
import React from 'react';
import {motion, AnimatePresence} from 'framer-motion';
export const wrapPageElement = ({element}) => (
<AnimatePresence exitBeforeEnter>{element}</AnimatePresence>
);
And then have a motion.main component around the page content in my layout.js file:
/** @jsx jsx */
import * as React from "react"
import { Global } from "@emotion/react"
import { Box, Container, jsx } from "theme-ui"
import Seo from "./seo"
import Header from "./header"
import Footer from "./footer"
import CodeStyles from "../styles/code"
import SkipNavLink from "./skip-nav"
import { motion, AnimatePresence } from 'framer-motion'
type LayoutProps = { children: React.ReactNode; className?: string }
const Layout = ({ children, className = `` }: LayoutProps) => (
<React.Fragment>
<Global
styles={(theme) => ({
"*": {
boxSizing: `inherit`,
},
html: {
WebkitTextSizeAdjust: `100%`,
},
img: {
borderStyle: `none`,
},
pre: {
fontFamily: `monospace`,
fontSize: `1em`,
},
"[hidden]": {
display: `none`,
},
"::selection": {
backgroundColor: theme.colors.text,
color: theme.colors.background,
},
a: {
transition: `all 0.3s ease-in-out`,
color: `text`,
},
})}
/>
<Seo />
<SkipNavLink>Skip to content</SkipNavLink>
<Container>
<Header />
<motion.main
key={location.pathname}
initial={{ opacity: 0, y: 2.5 }}
animate={{ opacity: 1, y: 0 }}
exit={{ opacity: 0, y: -2.5 }}
transition={{
type: "spring",
damping: 8,
mass: .6,
stiffness: 70,
}}
>
<Box id="skip-nav" sx={{ ...CodeStyles }} className={className}>
{children}
</Box>
</motion.main>
<Footer />
</Container>
</React.Fragment>
)
export default Layout
Does anyone have ideas as to why this might not be working when I deploy? (FYI I am using Netlify). I've tried many different semantic variations of this idea to no avail. Could it be how I'm declaring the key for the motion component? How I'm wrapping the AnimatePresence and motion components?
Upvotes: 0
Views: 1757
Reputation: 29335
wrapPageElement
is a shared API between Gatsby Browser (gatsby-browser.js
) and Gatsby SSR (gatsby-ssr.js
). Try adding the same wrapping component in the gatsby-ssr.js
:
import React from 'react';
import {motion, AnimatePresence} from 'framer-motion';
export const wrapPageElement = ({element}) => (
<AnimatePresence exitBeforeEnter>{element}</AnimatePresence>
);
In the docs:
Note: [...] It is recommended to use both APIs together.
The fact that works in the local environment and not in Netlify leads me to think that you can potentially have mismatching Node versions between both environments, causing a different dependency tree. If building the site locally makes your animation work, the issue is on Netlify's side (or in the Node versions installed, configuration). If the local built site doesn't (same behavior as Netlify's) the issue is on your code.
You can try setting in both environments the same Node version. To do so automatically, you can run:
node -v > .nvmrc
This will create a .nvmrc
file containing the Node version (node -v
). When Netlify finds this file at the root of your project, it uses the Node version as a base for installing the dependencies (docs: https://docs.netlify.com/configure-builds/manage-dependencies/#node-js-and-javascript)
Try both workarounds and see how's the debugging. As I said, if both environments has the exact same configuration the built shouldn't work either locally so the issue should be in your code (missing wrapPageElement
wrapping in gatsby-ssr.js
, etc).
Upvotes: 1
Reputation: 13588
Try making motion.main a direct descendent of AnimatePresence.
E.g.
<AnimatePresence exitBeforeEnter>
<motion.main
key={location.pathname}
initial={{ opacity: 0, y: 2.5 }}
animate={{ opacity: 1, y: 0 }}
exit={{ opacity: 0, y: -2.5 }}
transition={{
type: "spring",
damping: 8,
mass: .6,
stiffness: 70,
}}
>
<Box id="skip-nav" sx={{ ...CodeStyles }} className={className}>
{children}
</Box>
</motion.main>
</AnimatePresence>
This is written in the docs. Probably because there are too many wrappers before your motion.main
component.
The children of AnimatePresence can also be custom components. The only requirement is that somewhere within this component is at least one motion component with an exit prop.
Note: The custom component being removed from the DOM must still be a direct descendant of AnimatePresence for the exit animation(s) it contains to trigger.
Upvotes: 0