title: Shared element route transition with framer-motion and NextJS (typescript)
body: <p>I can't figure out how to animate routing with <code>&lt;AnimateSharedLayout /&gt;</code> component by framer-motion.
  Basically in the code down here i want to display a list of images, and when clicking on them i want to navigate to <code>/images/[image_id]</code> and display it wide. I don't have the images locally so I have to fetch them. There are some problems here:</p>
  <ol>
  <li>The same element gets fetched one first time on the index page, then gets refetched on the detail page (and it would be nice if there was a way to bring it over to the detail page)</li>
  <li>Before changing route, the page automatically scrolls to the top, ruining the animation;</li>
  <li>If the user is on a slow network, the animation is laggy or not even kicking in;</li>
  <li>going back to the previous route doesn't replay the animation (I may have to cache api calls to avoid refetching them all?)</li>
  </ol>
  <p>here's the code <code>_app.tsx</code></p>
  <pre><code>import '../styles/globals.css';
  import 'bulma/css/bulma.css';
  import { AppProps } from &quot;next/app&quot;;
  import { AnimateSharedLayout, AnimatePresence } from 'framer-motion';
  
  const MyApp: React.FC&lt;AppProps&gt; = ({ Component, pageProps, router }) =&gt; {
      return (
          &lt;AnimateSharedLayout&gt;
              &lt;AnimatePresence&gt;
                  &lt;Component {...pageProps} key={router.route} /&gt;
              &lt;/AnimatePresence&gt;
          &lt;/AnimateSharedLayout&gt;
      );
  }
  
  export default MyApp;
  
  </code></pre>
  <p>here's <code>pages/index.tsx</code></p>
  <pre><code>import { motion } from &quot;framer-motion&quot;;
  import { useEffect, useState } from &quot;react&quot;;
  import Picture, { PictureProps } from &quot;./components/Picture&quot;;
  
  export const base = 'https://jsonplaceholder.typicode.com';
  
  const Home: React.FC = () =&gt; {
      const [pics, setPics] = useState&lt;PictureProps[]&gt;([]);
      useEffect(() =&gt; {
          fetch(`${base}/photos?_start=0&amp;_limit=15`)
              .then(res =&gt; res.json())
              .then(res =&gt; setPics(res as PictureProps[]))
              .catch(err =&gt; console.log(&quot;fetching error&quot;, err));
      }, []);
      return (
          &lt;div className=&quot;container pt-4&quot;&gt;
              &lt;div className=&quot;columns is-multiline&quot;&gt;
                  {
                      pics.map(pic =&gt; (
                          &lt;div key={pic.id} className=&quot;column is-one-third-desktop is-half-tablet is-full-mobile&quot;&gt;
                              &lt;Picture {...pic} /&gt;
                          &lt;/div&gt;
                      ))
                  }
              &lt;/div&gt;
          &lt;/div&gt;
      );
  }
  
  export default Home;
  
  </code></pre>
  <p>here's <code>pages/images/[image].tsx</code></p>
  <pre><code>import { motion } from &quot;framer-motion&quot;;
  import { useRouter } from &quot;next/router&quot;;
  import { useEffect, useState } from &quot;react&quot;;
  import { PictureProps } from &quot;../components/Picture&quot;;
  import { base } from '../index';
  
  const Image: React.FC = () =&gt; {
      const router = useRouter();
      const { image } = router.query;
      const [pic, setPic] = useState&lt;PictureProps&gt;({} as PictureProps);
      useEffect(() =&gt; {
          image &amp;&amp; fetch(`${base}/photos/${image}`)
              .then(res =&gt; res.json())
              .then(res =&gt; setPic(res))
              .catch(err =&gt; console.log(err));
      }, [image]);
      return (
          &lt;motion.div&gt;   
              {
                  image &amp;&amp;
                  &lt;motion.figure layoutId={`img-${image}`} className='image'&gt;
                      &lt;img src={pic.url} alt={pic.title} /&gt;
                  &lt;/motion.figure&gt;
              }
          &lt;/motion.div&gt;
      );
  }
  
  export default Image;
  
  </code></pre>
  <p>and here's <code>components/Picture.tsx</code></p>
  <pre><code>import { motion, Variants } from &quot;framer-motion&quot;;
  import Link from &quot;next/link&quot;;
  
  export type PictureProps = {
      albumId: number,
      id: number,
      title: string,
      url: string,
      thumbnailUrl: string,
  };
  
  const cardVariants: Variants = {
      unloaded: {
          y: -100,
          opacity: 0,
          transition: {
              when: &quot;afterChildren&quot;,
          },
      },
      loaded: {
          y: 0,
          opacity: 1,
          transition: {
              when: &quot;beforeChildren&quot;,
          },
      }
  };
  
  const textVariants: Variants = {
      unloaded: {
          scale: .7,
          opacity: 0,
      },
      loaded: {
          scale: 1,
          opacity: 1,
      },
  };
  
  const Picture: React.FC&lt;PictureProps&gt; = ({
      id, title, url,
      albumId, thumbnailUrl
  }) =&gt; {
      console.log(`img-${id}`);
      return (
          &lt;motion.div className='card' variants={cardVariants} initial=&quot;unloaded&quot; animate=&quot;loaded&quot; exit=&quot;exit&quot;&gt;
              &lt;div className=&quot;card-image&quot;&gt;
                  &lt;motion.figure 
                      className=&quot;image&quot;
                      layoutId={`img-${id}`}
                  &gt;
                      &lt;img src={url} alt={title} /&gt;
                  &lt;/motion.figure&gt;
              &lt;/div&gt;
              &lt;motion.div className=&quot;card-content&quot;&gt;
                  &lt;div className=&quot;media&quot;&gt;
                      &lt;div className=&quot;media-left&quot;&gt;
                          &lt;Link href={`images/${id}`}&gt;
                              &lt;a&gt;
                                  &lt;figure className=&quot;image is-50by50&quot;&gt;
                                      &lt;img src={thumbnailUrl} alt=&quot;thumbnail&quot; /&gt;
                                  &lt;/figure&gt;
                              &lt;/a&gt;
                          &lt;/Link&gt;
                      &lt;/div&gt;
                      &lt;motion.div variants={textVariants} className=&quot;media-content&quot;&gt;
                          &lt;p className=&quot;title is-6&quot;&gt;{title}&lt;/p&gt;
                      &lt;/motion.div&gt;
                  &lt;/div&gt;
                  &lt;motion.div variants={textVariants} className='content'&gt;
                      Some random text Some random text 
                      Some random text Some random text
                      &lt;br /&gt;
                      &lt;strong&gt;Album id&lt;/strong&gt;:&amp;nbsp;&lt;em&gt;{albumId}&lt;/em&gt;
                  &lt;/motion.div&gt;
              &lt;/motion.div&gt;
          &lt;/motion.div&gt;
      );
  }
  
  export default Picture;
  
  </code></pre>