Routing is the backbone of modern web applications, and you need it every time you create an SSR or a Single-Page Application.
Plain routing seems fine, but when the user navigates to different routes, we need to show some kind of progressbar or animated effects to make the experience feel more cohesive and fluid. This helps users maintain their context as they navigate through the app.
Animations make the layout more interactive, and the application feels more dynamic and engaging. They also clearly inform users that an action has been recognized and is being processed.
In this article, we will create a routing application with React Router v6 and use framer-motion to create animated route transitions.
Here is the step-by-step guide:
Step 1: Initialize a React project
You can initialize a new React project with Vite frontend build tool using the command:
npm create vite@latest animated-transitions -- --template react
Go inside the project folder and install the dependencies:
cd animated-transitions npm install
Step 2: Install React Router, framer-motion, and MUI
Let’s install the React Router, MUI, and framer-motion for animations using the command below:
npm install @mui/material @emotion/react @emotion/styled react-router-dom framer-motion
Step 3: Create Reusable Components
Let’s create basic reusable components: Home, About, and Contact. These are three for three different routes. Each component for each route.
Here is the source code for src/components/Home.jsx. Create a components folder under the src folder.
// src/components/Home.jsx import { Container, Typography } from "@mui/material"; const Home = () => ( <Container> <Typography variant="h4" component="h1" gutterBottom> Home Page </Typography> <Typography variant="body1">Welcome to the Home Page!</Typography> </Container> ); export default Home;
Here is the source code for src/components/About.jsx. Create a components folder under the src folder.
// src/components/About.jsx import { Container, Typography } from "@mui/material"; const About = () => ( <Container> <Typography variant="h4" component="h1" gutterBottom> About Page </Typography> <Typography variant="body1">Learn more about us on this page.</Typography> </Container> ); export default About;
Here is the source code for src/components/Contact.jsx. Create a components folder under the src folder.
// src/components/Contact.jsx import { Container, Typography } from "@mui/material"; const Contact = () => ( <Container> <Typography variant="h4" component="h1" gutterBottom> Contact Page </Typography> <Typography variant="body1">Get in touch with us!</Typography> </Container> ); export default Contact;
Step 4: Create a Navbar
Let’s create a navigation bar that will show three links:
- Home
- About
- Contact
Create a new file called “Navbar.jsx” under the “components” folder:
// src/components/NavBar.jsx import { AppBar, Toolbar, Button } from "@mui/material"; import { Link } from "react-router-dom"; const NavBar = () => ( <AppBar position="static"> <Toolbar> <Button color="inherit" component={Link} to="/"> Home </Button> <Button color="inherit" component={Link} to="/about"> About </Button> <Button color="inherit" component={Link} to="/contact"> Contact </Button> </Toolbar> </AppBar> ); export default NavBar;
Step 5: Create a PageWrapper Component for Animations
As discussed earlier, to add an animation to React components, use the “framer-motion” library. It serves as a wrapper around your elements, enabling them to have motion effects.
Under the “components” folder, create a new file called “PageWrapper.jsx” and add the code below:
// src/components/PageWrapper.jsx import { motion } from "framer-motion"; // eslint-disable-next-line react/prop-types const PageWrapper = ({ children }) => ( <motion.div initial={{ opacity: 0 }} animate={{ opacity: 1 }} exit={{ opacity: 0 }} transition={{ duration: 0.5 }} > {children} </motion.div> ); export default PageWrapper;
Basically, we create a component that wraps its children with a motion.div.
The motion.div element accepts the following props:
- initial: It defines the initial state of the animation.
- animate: It defines the target state of the animation
- exit: It defines the state of the element when it is exiting or being removed from the DOM.
- transition: It defines the properties of the transition, such as its duration.
It provides animated transitions for its children elements. These children components will be Home.jsx, Contact.jsx, and About.jsx.
That means all these components will have animation effects when we navigate to their respective routes.
If you want animation effects for any React components, wrap that component with the PageWrapper component.
When users switch between routes in our application, we animate the transition between different pages due to the PageWrapper component.
Step 6: Set up routing with React Route
Let’s set up a router inside the src/App.jsx component:
// src/App.jsx import { Routes, Route, useLocation } from "react-router-dom"; import { CssBaseline, Container } from "@mui/material"; import { AnimatePresence } from "framer-motion"; import Home from "./components/Home"; import About from "./components/About"; import Contact from "./components/Contact"; import NavBar from "./components/NavBar"; import PageWrapper from "./components/PageWrapper"; const App = () => { const location = useLocation(); return ( <> <CssBaseline /> <NavBar /> <Container> <AnimatePresence> <Routes location={location} key={location.pathname}> <Route path="/" element={ <PageWrapper> <Home /> </PageWrapper> } /> <Route path="/about" element={ <PageWrapper> <About /> </PageWrapper> } /> <Route path="/contact" element={ <PageWrapper> <Contact /> </PageWrapper> } /> </Routes> </AnimatePresence> </Container> </> ); }; export default App;
In this code, we used the AnimatePresence component from the framer-motion library to enable animations for components that are removed from the React tree. These are basically the custom components that we created in this app. For example, Home, About, or Contact.
The react-router-dom library provides routing modules and functions that will enable the routing functionality.
- The “Routes” is a container for “Route” components
- The “Route” represents a route in our application.
- The “useLocation” hook provided by the react-router-dom library is helpful in getting the current location object, which contains information about the current URL.
In short, we set up an App component in such a way that we implemented routing, styling, and animations in our project seamlessly.
Finally, update the “src/main.jsx” entry point for our application:
// src/main.jsx import { createRoot } from "react-dom/client"; import { BrowserRouter as Router } from "react-router-dom"; import App from "./App.jsx"; createRoot(document.getElementById("root")).render( <Router> <App /> </Router> );
Save the file and start the development server using the command below:
npm run dev
Here is the final output of the project in GIF:
That’s all!