Animated shared layout using framer-motion and React.js

The following approach covers how to create an animated shared layout using framer-motion and ReactJS.
Prerequisites:
- Knowledge of JavaScript (ES6)
- Knowledge of HTML/CSS.
- Basic knowledge of ReactJS.
Creating React Application And Installing Module:
-
Step 1: Create a React application using the following command:
npx create-react-app animated-layout
-
Step 2: After creating your project folder i.e. animated-layout, move to it using the following command.
cd animated-layout
-
Step 3: Add the npm packages you will need during the project :
npm install framer-motion
Now open the src folder and delete the following files and create a JavaScript file named Item.js.
- logo.svg
- serviceWorker.js
- setupTests.js
- App.test.js (if any)
- index.css
Project Structure: Your project structure tree should look like this:
Folder structure
Example:
- We are going to create an Item component that is the animated layout using react useState hook and framer-motion components motion and AnimatePresence.
- Content component is used to create Item’s (animated shared layout) content using HTML img tag and div & framer-motion component motion.
- The toggleOpen is a utility function to set the ‘isOpen’ value not (!) of its last value.
- In App.js, itemList is for the number of animated shared layout we want to create, in our case it is 3.
- In App.js, we are going to use framer-motion AnimatedSharedLayout component to wrap the imported Item component and map through ‘itemsList’ array to render animated layouts.
App.js
import React from "react"; import { AnimateSharedLayout } from "framer-motion"; import Item from "./Item"; import "./styles.css"; // This is an example of animating shared layouts // using react and framer-motion library. const itemsList = [ { index: 0, content: `Motion components are DOM primitives optimised for 60fps animation and gestures.` }, { index: 1, content: `Motion can animate: Numbers: 0, 10 etc. Strings containing numbers: "0vh", "10px" etc.` }, { index: 2, content: `Transform properties are accelerated by the GPU, and therefore animate smoothly. ` } ]; const App = () => { return ( // The framer-motion component to wrap Item component to animate it <AnimateSharedLayout> {/* Mapping through itemList array to render layouts*/} {itemsList.map((item) => ( <Item key={item.index} content={item.content} /> ))} </AnimateSharedLayout> ); }; export default App; |
Item.js
import React, { useState } from "react"; import { motion, AnimatePresence } from "framer-motion"; const Content = ({ content }) => { "20200817185016/gfg_complete_logo_2x-min.png" return ( <motion.div layout initial={{ opacity: 0 }} animate={{ opacity: 1 }} exit={{ opacity: 0 }} > <img src={url} alt="zambiatek" /> <div className="row">{content}</div> </motion.div> ); }; const Item = ({ content }) => { // React useState hook is used to manage the state of 'isOpen' // that in turn toggles shared layout, user clicks on const [isOpen, setIsOpen] = useState(false); // Utility function to set 'isOpen' '!'(not) of its last value const toggleOpen = () => setIsOpen(!isOpen); "27UlohMeBLxyUdhs9hUbc-Agw=s900-c-k-c0x00ffffff-no-rj" return ( <motion.li layout title="Click to reveal" onClick={toggleOpen} initial={{ borderRadius: [25] }} > <motion.div className="avatar" layout> {" "} <img src={url} alt="gfg" />{" "} </motion.div> <br /> <AnimatePresence>{isOpen && <Content content={content} />} </AnimatePresence> </motion.li> ); }; export default Item; |
styles.css
body { min-height: 100vh; margin: 0; display: flex; justify-content: center; align-items: center; } * { box-sizing: border-box; } ul, li { list-style: none; margin: 0; padding: 0; } ul { width: 300px; display: flex; flex-direction: column; background: #fcfcfc; padding: 20px; border-radius: 25px; } li { background-color: rgba(214, 214, 214, 0.5); border-radius: 10px; padding: 20px; margin-bottom: 20px; overflow: hidden; cursor: pointer; width: 300px; } li:last-child { margin-bottom: 0px; } .avatar { width: 40px; height: 40px; border-radius: 20px; } .avatar img { width: 40px; border-radius: 100%; } .row { margin-top: 12px; } img { width: 250px; height: 40px; } |
Step to Run Application: Run the application using the following command from the root directory of the project:
npm start
Output: Now open your browser and go to http://localhost:3000/, you will see the following output:



