Blog.

Flipping text with framer-motion

Cover Image for Flipping text with framer-motion
Gabriel L. Maljkovich
Gabriel L. Maljkovich

What we are building

Let's get started

First, we need to install the framer-motion library. You can do this by running the following command:

npm install framer-motion

Next, we are going to create a page to showcase the component. Let's make a file called ìndex.js and add the following code:

index.js
import "./styles.css";
 
export default function App() {
  return (
    <div className="App">
      <h1>Rick Astley</h1>
      <h2>
        He's never gonna <span>give you up</span>
      </h2>
    </div>
  );
}

And let's add some styles for the page. Create a file called styles.css and add the following code:

styles.css
body,
html {
  margin: 0;
}
.App {
  display: flex;
  flex-direction: column;
  justify-content: center;
  align-items: center;
  height: 100vh; /* adjust as needed */
  text-align: center; /* for centering the text */
  font-family: sans-serif;
  color: #023047;
}
 
.App h1 {
  font-size: 48px;
  font-weight: 300;
  line-height: 1rem;
  letter-spacing: 0.45rem;
}
 
.App h2 {
  display: inline-block;
  padding-right: 10rem;
  color: #4a4e69;
  font-size: 32px;
  font-weight: 300;
}

The TextLoop component

Now, let's create a new file called TextLoop.js and add the following code:

TextLoop.js
import React, { useState, useEffect } from "react";
import { AnimatePresence, motion } from "framer-motion";
 
const variants = {
  initial: {
    y: -20,
    opacity: 0,
  },
  fadeIn: {
    zIndex: 1,
    y: 0,
    opacity: 1,
  },
  exit: {
    zIndex: 0,
    opacity: 0,
  },
};
 
export const TextLoop = ({ words, className }) => {
  const [index, setIndex] = useState(0);
 
  useEffect(() => {
    const timeoutId = setTimeout(() => {
      let next = index + 1;
      if (next === words.length) {
        next = 0;
      }
      setIndex(next);
    }, 3 * 1000);
    return () => clearTimeout(timeoutId);
  }, [index, setIndex]);
 
  return (
    <AnimatePresence>
      <span className={className}>
        <motion.span
          style={{ position: "absolute" }}
          variants={variants}
          key={index}
          initial="initial"
          animate="fadeIn"
          exit="exit"
          layout
          transition={{
            y: { type: "spring", stiffness: 300, damping: 100 },
            opacity: { duration: 0.5 },
          }}
        >
          {words[index]}
        </motion.span>
      </span>
    </AnimatePresence>
  );
};

That's a lot of code so let's break it down:

  1. We define the variants object that will be used to animate the text.
  2. We create the TextLoop component that receives an array of words and a className as props.
  3. We added a state variable index to keep track of the current word to show.
  4. We use the useEffect hook to update the index every 3 seconds.
  5. We use the AnimatePresence component to fade out the text when it is removed from the DOM.

Using the TextLoop component

Go back to the ìndex.js file and add the following code:

index.js
import "./styles.css";
import { TextLoop } from "./TextLoop";
 
const Word = ({ color, children }) => (
  <span style={{ color: color }} className="word">
    {children}
  </span>
);
 
const words = [
  <Word color="blue">give you up</Word>,
  <Word color="red">let you down</Word>,
  <Word color="lime">run around</Word>,
  <Word color="purple">desert you</Word>,
];
 
export default function App() {
  return (
    <div className="App">
      <h1>Rick Astley</h1>
      <h2>
        He's never gonna <TextLoop className="text-loop" words={words} />
      </h2>
    </div>
  );
}

What we did is create a new Word component to handle the styles of the sliding text. Then we created an array of "words" and passed it to the TextLoop component along with a className for styling the container.

Now let's add those styles to the styles.css file:

styles.css
 
.text-loop {
  margin-left: 8px;
  position: relative;
  display: inline;
  white-space: nowrap;
}
 
.word {
  font-style: italic;
  font-weight: bold;
}

And that's it! You should now see the text loop animation in action.