TABLE OF CONTENTS

How To Use Framer Motion With React, TypeScript and Styled Components?

How To Use Framer Motion With React, TypeScript and Styled Components?

Introduction

As you already know, Framer Motion is a really powerful animation library, used by many React App Development companies.

Now it’s time to start using it.

Below are some basics.

How to start with Framer Motion

To start with Framer Motion for React you have to install the right package:

npm

npm install framer-motion --save

yarn

 yarn add framer-motion

The next step is to import motion from framer-motion in the file that we want to use it:

 import { motion } from 'framer-motion';

And now the fun begins!

We can animate any HTML or SVG elements. We can use Styled Components, CSS Modules, SCSS, CSS or whatever we want to add styles. There are two ways of creating posed components:

1. When you’re using classNames:

import motion to the file that you want to use it in like this:

import { motion } from 'framer-motion';

add HTML element after motion. and put it inside angle brackets, just like it was a regular HTML tag or component, add needed className:

<motion.div className="box" />

As a result, it will create a div with a .box class name. You can use it to add styles to this element. Animation properties will be added via variant and animate config, so you don’t have to take care of them in CSS.

2. When you’re using StyledComponents:

the first step is the same as 1.a., but you also have to import styled to use styled-components:

import { motion } from 'framer-motion';
import styled from 'styled-components';

create StyledComponent with motion element as a tag:

const BoxStyled = styled(motion.div)`
    display: flex;
`;

use it the same way as in 1.b. This time you don’t add className:

return <BoxStyled />;

Animate

To prepare the animation we have to use the animate property.

It should be added to the motion component (in this case it will be BoxStyled).

To make a simple animation we can pass some attributes directly into the animate prop like this:

return <BoxStyled animate={{ scale: 0.5 }} />;

It will start when the component will be rendered on the page.

You can wonder what happens with animation transition.

Well, Motion will create an appropriate animation for a snappy transition based on the types of value being animated.

“For instance, physical properties like x or scale will be animated via a spring simulation. Whereas values like opacity or color will be animated with a tween.”

It can be passed as an attribute inside the animate property or as a separate transition property.

If no transition will be set inside animate, then Motion will take its config from transition prop:

return (
   <BoxStyled 
     animate={{ scale: 0.5 }} 
     transition={{ duration: 2 }} 
   />
);

And that’s it. You’ve created your first Motion animation!

Want to use Framer Motion in your project?

Variants

To make more advanced animation that can be adjusted based on changes inside a component, we need to prepare some variants – animation states that our component will be in, like open and closed, visible and hidden, etc.

Let’s make it step by step:

Step 1

Prepare variant config object – you can name it whatever you want, you will later use this name inside StyledComponent. Inside you should add animation states names like visible and hidden:

const variants = {
  visible: {},
  hidden: {}
};

Step 2

When you have your states declared, it’s time to add some animation properties. In this example, we want to show and hide the element, so those are the attributes that we want to animate:

const variants = {
  visible: { opacity: 1, transition: { duration: 3 } },
  hidden: { opacity: 0 }
};

Step 3

Add variant config to StyledComponent as an attribute like this:

const BoxStyled = styled(motion.div).attrs(() => ({
  initial: "hidden",
  variants}))`
    display: flex;
`;

You can set initial variant if you need your animation to start from it (it’s not necessary)

Step 4

Set needed animation variant inside a Component:

return <BoxStyled animate=”visible” />;

You can also change the animation dynamically, for example when the state changes:

import * as React from "react";
import { useState } from "react";

import { BoxStyled } from "./styles";

const Box: React.FC = () => {
  const [isVisible, setIsVisible] = useState<boolean>(false);

  return (
    <>
      <button
        type="button"
        onClick={() => setIsVisible(prevState => !prevState)}
      >
        Click here to make content visible
      </button>
      <BoxStyled 
        animate={isVisible ? "visible" : "hidden"}
      >
        I’m visible!
      </BoxStyled>
    </>
  );
};

export default Box;

And voilà! Next animation ready!

This is how it should be looking like:

What about the children?!

Well, since you asked:

“If a motion component has children, changes in variant will flow down through the component hierarchy. These changes in variant will flow down until a child component defines its own animate property.”

If you don’t want children to inherit animation from their parent you can add inherit prop and set it to false.

Animation set on children will start simultaneously with parent animation.

If we want the parent to orchestrate the execution of child animations we can use some extra transition props that we gain access to when using variants:

  • when
  • delayChildren
  • staggerChildren
const variants = {
  visible: { 
    opacity: 1, 
    transition: {
      when: "beforeChildren",
      staggerChildren: 0.3,
    }
  },
  hidden: { 
    opacity: 0, 
    transition: {
      when: "afterChildren",
    }
  }
};

Here’s a live example:

There are many cool properties that you can use. You can find all of them in the documentation.

AnimatePresence

If you’re working on react projects, then you know that sometimes we want to render a component or its part and sometimes we don’t or we just shouldn’t.

It creates a small problem with animations because we can’t add any effect on an element that just disappears.

Motion provides a component called AnimatePresence that helps us in those situations. It’s really easy to use and provides some hooks that we can use inside its children.

import { motion, AnimatePresence } from "framer-motion"

export const MyComponent = ({ isVisible }) => (
  <AnimatePresence>
    {isVisible && (
      <StyledBox
        initial={{ opacity: 0 }}
        animate={{ opacity: 1 }}
        exit={{ opacity: 0 }}
      />
    )}
  </AnimatePresence>
)

In this example, our component is rendered only when prop isVisible is set to true.

To use this component properly, we have to add exit animation.

It will be used when component will be unmounting itself. AnimatePresence should always be present in the DOM.

Events and gestures

As already said at the beginning – this library offers more advanced listeners, extends the basic set of event listeners provided by React. We can use:

  • hover – detects when a pointer hovers over or leaves a component
    • whileHover
    • onHoverStart(event, info)
    • onHoverEnd(event, info)
  • tap – detects when a pointer presses down and releases on the same component
    • whileTap
    • onTap(event, info)
    • onTapStart(event, info)
    • onTapCancel(event, info)
  • pan – recognizes when a pointer presses down on a component and moves further than 3 pixels
    • onPan(event, info)
    • onPanStart(event, info)
    • onPanEnd(event, info)
  • drag – follows the rules of the pan gesture but applies pointer movement to the x and/or y-axis of the component
    • drag
    • dragConstraints
    • dragElastic
    • dragMomentum
    • dragTransition
    • dragPropagation
    • dragControls
    • onDrag(event, info)
    • onDragStart(event, info)
    • onDragEnd(event, info)
    • onDirectionLock(axis)

You can read more about them here.

It gives us many possibilities. We can even add a separate animation on elements on event start, on event end and while the event is active. It’s not possible with CSS only.

Here’s a live example with a really simple animation:

SVGs

There is also the ability to have some fun with SVG files. We can animate all elements just as usual. Motion provides some additional properties for paths:

  • pathLength
  • pathSpacing
  • pathOffset

All of those can be set as a value between 0 and 1.

“A note on SVG filters.
The while helper properties won’t work on SVG filter components, as these elements don’t have a physical presence and therefore don’t receive events. To respond to gestures, you need to introduce React state to the component and attach listeners to the physical element.”

So there are also some limitations. I’ve prepared a really simple animation using SVG below.

It is created with pathLength.

And What About Page Speed?

It’s important to make the page load as quickly as possible these days. Many developers are trying to find a way to avoid adding external packages because they weigh too much. Unfortunately, many animation libs are pretty heavy and we have to think twice if we can use them or not in the project.

We want the page to look good and have some nice animation effects, but at the same time, we don’t want to lose a user because the page took too long to load. Luckily, framer motion provides us with a way to reduce the bundle size!

If you are using the `motion` component a lot, then there’s something that you can change to make the page faster. First, you have to use the `LazyMotion` provider from ‘framer-motion’ and pass feature packages to the feature prop. Currently, there are two features packages:

  • domAnimation – it allows you to create animations, use variants, exit animations, and tap/hover/focus gestures and it weighs 15kb
  • domMax – it provides all of the features included in domAnimation package and pan/drag gestures and layout animations in addition and it weighs 25kb

It can be used like this:

import { PropsWithChildren } from "react"
import { LazyMotion, domAnimation } from "framer-motion"

export const App: React.FC<PropsWithChildren> = ({ children }) => {
  return (
    <LazyMotion features={domAnimation}>
      {children}
    </LazyMotion>
  )
}

You can also make it work asynchronously by passing a dynamic import function to `features` prop. It will fetch features only after we’ve performed the initial render.

To make it work, you have to import the feature package into a separate file and export it from there. Next, you have to write a function that will dynamically add the package and pass this function to `features` prop

// features-package.ts
import { domAnimation } from "framer-motion"
export default domAnimation

// app.tsx
import { PropsWithChildren } from "react"
import { LazyMotion, domAnimation } from "framer-motion"

const loadFeaturesPackage = () =>
  import("./features-package.ts").then(res => res.default)

export const App: React.FC<PropsWithChildren> = ({ children }) => {
  return (
    <LazyMotion features={loadFeaturesPackage}>
      {children}
    </LazyMotion>
  )
}

Now, that the main setup is done, you can go to a file where you’re using `motion` component. We will have to replace this with `m`, so instead of:

import { motion } from 'framer-motion';

…

<motion.div
    initial={{ opacity: 0 }}
    animate={{ opacity: 1 }}
/>

We will now use:

import { m } from 'framer-motion';

…

<m.div
    initial={{ opacity: 0 }}
    animate={{ opacity: 1 }}
/>

And that’s pretty much it! It’s important not to use both versions in the same app and to make sure that you’re using `m` instead of `motion` everywhere, you can add `strict` prop to `LazyMotion` provider.

import { PropsWithChildren } from "react"
import { LazyMotion, domAnimation } from "framer-motion"

export const App: React.FC<PropsWithChildren> = ({ children }) => {
  return (
    <LazyMotion strict features={domAnimation}>
      {children}
    </LazyMotion>
  )
}

So, what now?

I believe that Motion gives us many possibilities and makes our life easier in many cases.

It’s definitely worth learning.

You can start from those simple animations provided in this article and then create something on your own.

There are also many examples on the Framer Motion documentation page. Maybe you will get inspired by some of them.

FAQ

What are the basic requirements to start using Framer Motion in a React project?

To start using Framer Motion, you need to install the package using npm npm install framer-motion --save or yarn yarn add framer-motion. Then, import motion from framer-motion into the React file where you intend to use animations. Framer Motion allows for animating HTML or SVG elements, and it supports various styling methods including Styled Components, CSS Modules, SCSS, and CSS.

How do you animate components using Framer Motion?

Components can be animated by using the animate property on the motion component, such as <motion.div animate={{ scale: 0.5 }} />. This property takes an object of style properties to animate to, and animations start automatically when the component renders. For more controlled animations, you can specify transitions using the transition property.

What is the AnimatePresence component and when should you use it?

The AnimatePresence component is used to animate components that mount and unmount from the React tree. It’s particularly useful for animations that need to run on components as they are being removed from the DOM. By wrapping components in AnimatePresence and using the exit property on motion components, you can define how components should animate out before they are removed.

Read more

TypeScript vs JavaScript

Article link copied

Close button

Leave a Reply

* Required informations.