Animations and Transitions Between Routes

Introduction

In modern web applications, smooth animations and transitions significantly enhance the user experience. React applications, especially single-page applications (SPAs), can leverage route transitions to make navigation feel more fluid and engaging.

Adding animations to route changes improves the perception of performance and provides visual continuity when switching between pages. Libraries like Framer Motion or CSS transitions make implementing these animations straightforward.

In this post, we will cover:

  • Adding page transitions with Framer Motion or CSS
  • Example transitions: fade-in/out and slide effects
  • Combining route changes with smooth animations
  • Optimizing transitions for nested and dynamic routes

Using Framer Motion for Route Animations

Framer Motion is a powerful React library for animations. It supports motion components, variants, and animation hooks, which make route transitions smooth and declarative.

Installing Framer Motion

npm install framer-motion
  • Provides <motion.div> and other animated components.
  • Supports simple to complex animations.

Basic Fade-In/Fade-Out Transition

A common effect is a fade-in/fade-out when a new page is rendered.

Example

import React from 'react';
import { motion } from 'framer-motion';

function Page({ children }) {
  return (
&lt;motion.div
  initial={{ opacity: 0 }}
  animate={{ opacity: 1 }}
  exit={{ opacity: 0 }}
  transition={{ duration: 0.5 }}
&gt;
  {children}
&lt;/motion.div&gt;
); } export default Page;
  • initial: Starting state when component mounts
  • animate: Target state for the animation
  • exit: State when component unmounts
  • transition: Duration and easing

Integrating Framer Motion with React Router

React Router provides route changes, but by default components mount/unmount immediately. Combining it with AnimatePresence from Framer Motion allows animations during route transitions.

Setup Example

import React from 'react';
import { BrowserRouter as Router, Routes, Route, useLocation } from 'react-router-dom';
import { AnimatePresence } from 'framer-motion';
import Home from './Home';
import About from './About';
import Contact from './Contact';
import Page from './Page';

function AnimatedRoutes() {
  const location = useLocation();

  return (
&lt;AnimatePresence exitBeforeEnter&gt;
  &lt;Routes location={location} key={location.pathname}&gt;
    &lt;Route path="/" element={&lt;Page&gt;&lt;Home /&gt;&lt;/Page&gt;} /&gt;
    &lt;Route path="/about" element={&lt;Page&gt;&lt;About /&gt;&lt;/Page&gt;} /&gt;
    &lt;Route path="/contact" element={&lt;Page&gt;&lt;Contact /&gt;&lt;/Page&gt;} /&gt;
  &lt;/Routes&gt;
&lt;/AnimatePresence&gt;
); } function App() { return (
&lt;Router&gt;
  &lt;AnimatedRoutes /&gt;
&lt;/Router&gt;
); } export default App;

Key Points:

  • AnimatePresence enables exit animations for unmounted components.
  • key={location.pathname} ensures each route triggers a new animation.
  • Wrapping pages in <Page> applies consistent animation styles.

Slide Transitions Between Routes

Slide transitions create a sense of movement when navigating between pages.

Slide Example

function PageSlide({ children }) {
  return (
&lt;motion.div
  initial={{ x: 300, opacity: 0 }}
  animate={{ x: 0, opacity: 1 }}
  exit={{ x: -300, opacity: 0 }}
  transition={{ type: 'spring', stiffness: 100 }}
&gt;
  {children}
&lt;/motion.div&gt;
); }
  • x represents horizontal movement.
  • spring transition adds natural bounce.
  • Customize stiffness and damping for smoother motion.

Combining Route Changes with Animations

To apply consistent animations for multiple routes:

  1. Wrap all route components in an animated wrapper.
  2. Use unique keys for each route.
  3. Apply initial, animate, and exit states.
  4. Configure transitions for timing and easing.

Example

<AnimatePresence exitBeforeEnter>
  <Routes location={location} key={location.pathname}>
&lt;Route path="/" element={&lt;PageSlide&gt;&lt;Home /&gt;&lt;/PageSlide&gt;} /&gt;
&lt;Route path="/about" element={&lt;PageSlide&gt;&lt;About /&gt;&lt;/PageSlide&gt;} /&gt;
&lt;Route path="/contact" element={&lt;PageSlide&gt;&lt;Contact /&gt;&lt;/PageSlide&gt;} /&gt;
</Routes> </AnimatePresence>
  • Navigation triggers smooth entry and exit animations.
  • Enhances perceived speed and user experience.

Animating Nested Routes

Nested routes require careful handling to animate both parent and child components.

Example

function Dashboard() {
  return (
&lt;motion.div
  initial={{ opacity: 0 }}
  animate={{ opacity: 1 }}
  exit={{ opacity: 0 }}
  transition={{ duration: 0.3 }}
&gt;
  &lt;h1&gt;Dashboard&lt;/h1&gt;
  &lt;Routes&gt;
    &lt;Route path="profile" element={&lt;Profile /&gt;} /&gt;
    &lt;Route path="settings" element={&lt;Settings /&gt;} /&gt;
  &lt;/Routes&gt;
&lt;/motion.div&gt;
); }
  • Parent page animation triggers first.
  • Nested routes can animate individually.
  • Combine AnimatePresence at parent level for smoother transitions.

Dynamic Route Transitions

Dynamic routes, such as /users/:id, can also be animated.

<Route
  path="/users/:id"
  element={
&lt;PageSlide&gt;
  &lt;UserProfile /&gt;
&lt;/PageSlide&gt;
} />
  • Each dynamic route is treated as a unique key using location.pathname.
  • Allows smooth transitions even when navigating between user profiles.

CSS-Based Transitions

While Framer Motion is powerful, CSS transitions can also achieve basic animations.

Example: Fade with CSS

.page-enter {
  opacity: 0;
}
.page-enter-active {
  opacity: 1;
  transition: opacity 0.5s ease-in;
}
.page-exit {
  opacity: 1;
}
.page-exit-active {
  opacity: 0;
  transition: opacity 0.5s ease-out;
}
import { CSSTransition, TransitionGroup } from 'react-transition-group';

<TransitionGroup>
  <CSSTransition key={location.key} classNames="page" timeout={500}>
&lt;Routes location={location}&gt;
  &lt;Route path="/" element={&lt;Home /&gt;} /&gt;
  &lt;Route path="/about" element={&lt;About /&gt;} /&gt;
&lt;/Routes&gt;
</CSSTransition> </TransitionGroup>
  • CSSTransition integrates CSS animations with route changes.
  • Works well for simple transitions like fade or slide.

Optimizing Transitions

  1. Avoid heavy animations on large components to prevent lag.
  2. Lazy load components for faster initial load.
  3. Use exitBeforeEnter in Framer Motion to ensure proper exit animations.
  4. Animate only visible components to reduce rendering cost.
  5. Combine with code-splitting for nested and dynamic routes.

Example: Complete Animated App

import React from 'react';
import { BrowserRouter as Router, Routes, Route, useLocation } from 'react-router-dom';
import { AnimatePresence, motion } from 'framer-motion';
import Home from './Home';
import About from './About';
import Contact from './Contact';

function PageSlide({ children }) {
  return (
&lt;motion.div
  initial={{ x: 300, opacity: 0 }}
  animate={{ x: 0, opacity: 1 }}
  exit={{ x: -300, opacity: 0 }}
  transition={{ type: 'spring', stiffness: 100 }}
&gt;
  {children}
&lt;/motion.div&gt;
); } function AnimatedRoutes() { const location = useLocation(); return (
&lt;AnimatePresence exitBeforeEnter&gt;
  &lt;Routes location={location} key={location.pathname}&gt;
    &lt;Route path="/" element={&lt;PageSlide&gt;&lt;Home /&gt;&lt;/PageSlide&gt;} /&gt;
    &lt;Route path="/about" element={&lt;PageSlide&gt;&lt;About /&gt;&lt;/PageSlide&gt;} /&gt;
    &lt;Route path="/contact" element={&lt;PageSlide&gt;&lt;Contact /&gt;&lt;/PageSlide&gt;} /&gt;
  &lt;/Routes&gt;
&lt;/AnimatePresence&gt;
); } function App() { return (
&lt;Router&gt;
  &lt;AnimatedRoutes /&gt;
&lt;/Router&gt;
); } export default App;
  • Smooth sliding transitions between Home, About, and Contact pages.
  • Uses Framer Motion for declarative animations.
  • Works for both top-level and nested routes.

Best Practices

  1. Use consistent animations for all pages to maintain UX coherence.
  2. Prefer declarative animations using Framer Motion over manual CSS manipulation.
  3. Animate only what is necessary to avoid performance issues.
  4. Combine lazy loading with transitions for optimized rendering.
  5. Test on different devices to ensure smooth animations.
  6. Use route keys to trigger exit/enter animations properly.
  7. Keep transitions subtle to avoid distraction.


Comments

Leave a Reply

Your email address will not be published. Required fields are marked *