Search code examples
javascriptreact-router-dom

use page transitions with react router dom


I am trying to use an animation i.e. page transition with react-router-dom using the object version for the declaration. I don't seem to find how to specify what animation to use.

So I have:

const router = createBrowserRouter([
    {
        element: <CustomLayout />,
        loader: _ => {
            resetParameters()
            return null
        },
        children: [
            {
                index: true,
                element: <HomePage />,
                loader: _ => {
                    resetParameters()
                    return null
                }]
}

Where do I specify the animation transition. I am looking for something like this sample from https://github.com/Steveeeie/react-page-transition but adapted to RRDv6 :

<PageTransition
              preset="moveToLeftFromRight"
              transitionKey={location.pathname}
            >
              <Switch location={location}>
                <Route exact path="/" component={Home} />
                <Route exact path="/about" component={About} />
              </Switch>
            </PageTransition>

which is fine if I were to use the tags, but I am using the object definition.

This sample is using https://github.com/Steveeeie/react-page-transition but it could be any other transition library.

An alternative would be to use the RRDv5 tags which are still supported in RRDv6 i.e. ... but this prevents the usage of the APIs in RRDv6 (all of the useXXX methods) which I need.


Solution

  • I'm a beginner so my code might be horrible but I managed to make a working version of the page transition using framer-motion, RouterProvider, and createBrowserRouter.

    App.jsx

    import { RouterProvider, createBrowserRouter } from 'react-router-dom'
    import './App.css'
    import HomePage from './pages/HomePage/HomePage'
    import DummyPage from './pages/DummyPage/DummyPage'
    import React from 'react'
    
    const router = createBrowserRouter([
      { path: '/', element: <HomePage /> },
      { path: '/dummy', element: <DummyPage /> },
    ])
    
    function App() {
      return <RouterProvider router={router} />
    }
    
    export default App
    

    Instead of using Link from react-router-dom I made a custom component for that.

    CustomLink.jsx

    import { useNavigate } from 'react-router-dom'
    import './CustomLink.css'
    function CustomLink({ dest, content, setExit }) {
      console.log(dest)
      const navigate = useNavigate()
    
      const handleDelayedLinkClick = (to, delay) => (event) => {
        event.preventDefault()
        setExit(true)
        setTimeout(() => {
          navigate(to)
        }, delay)
      }
    
      return (
        <div className='link' onClick={handleDelayedLinkClick(dest, 1000)}>
          {content}
        </div>
      )
    }
    
    export default CustomLink
    

    Now I used this custom link in my page components.

    HomePage.jsx

    import React, { useState } from 'react'
    import './HomePage.css'
    import { AnimatePresence, motion } from 'framer-motion'
    import CustomLink from '../../components/CustomLink'
    const HomePage = () => {
      const [exit, setExit] = useState(false)
      return (
        <AnimatePresence>
          {!exit && (
            <motion.div
              initial={{ opacity: 0 }}
              animate={{ opacity: 1 }}
              exit={{ opacity: 0 }}
              transition={{ duration: 1 }}
              className='container'
            >
              <div>This is HomePage</div>
              <div>
                Click to navigate to{' '}
                <CustomLink dest={'/dummy'} content={'Dummy'} setExit={setExit} />
              </div>
            </motion.div>
          )}
        </AnimatePresence>
      )
    }
    
    export default HomePage
    

    DummyPage.jsx

    import { AnimatePresence, motion } from 'framer-motion'
    import { Link } from 'react-router-dom'
    import './DummyPage.css'
    import CustomLink from '../../components/CustomLink'
    import { useState } from 'react'
    const DummyPage = () => {
      const [exit, setExit] = useState(false)
      return (
        <AnimatePresence>
          {!exit && (
            <motion.div
              initial={{ opacity: 0 }}
              animate={{ opacity: 1 }}
              exit={{ opacity: 0 }}
              transition={{ duration: 1 }}
              className='dummy-container'
            >
              <div>This is DummyPage</div>
              <div>
                Click to navigate to{' '}
                <CustomLink dest={'/'} content={'Home'} setExit={setExit} />
              </div>
            </motion.div>
          )}
        </AnimatePresence>
      )
    }
    
    export default DummyPage
    

    With this, I was able to do a fade-in and fade-out transition on-page navigation. If you don't have links and want to conditionally navigate to another page you can use a state to conditionally render the component, wrap it in animate presence. You can make a custom hook to handle this logic. Hope this helps.

    Code sandbox link here