Advanced UI Patterns and Animations

Build smooth, delightful UIs using advanced techniques and modern animation tools.

🎞️ Declarative Animations with Framer Motion

Welcome to the world of declarative animations in React! Framer Motion is a powerful library that makes creating smooth, performant animations a breeze. Whether you're building UI transitions or adding subtle interactions, this chapter will teach you everything you need to know to create beautiful and accessible animations.

💡 What is Framer Motion?

Framer Motion is a declarative animation library for React. It provides a simple, yet powerful way to add animations to your components without getting lost in complex APIs or performance pitfalls.

💡 Getting Started

Let's start by setting up Framer Motion in your project. You'll need to install it using npm or yarn.

npm install framer-motion
# or
yarn add framer-motion

Now that Framer Motion is installed, you can start using it in your components.

💡 Basic Animation

Framer Motion provides a motion component that wraps any React element and adds animation capabilities. Here's how to create a basic animation.

import { motion } from 'framer-motion';

function MyComponent() {
  return (
    <motion.div
      initial={{ opacity: 0, x: -20 }}
      animate={{ opacity: 1, x: 0 }}
      transition={{ duration: 0.5 }}
    >
      Hello, World!
    </motion.div>
  );
}

In this example: - The initial prop defines the starting state. - The animate prop defines the end state. - The transition prop controls how the animation is performed.

💡 Layout Transitions

When working with layouts that change dynamically (like lists or grids), layout transitions can help create a smooth experience. Framer Motion provides the layoutId and AnimatePresence components to handle these cases.

import { motion, AnimatePresence } from 'framer-motion';

function List() {
  const items = ['Item 1', 'Item 2', 'Item 3'];

  return (
    <motion.div layoutId="list">
      {items.map((item) => (
        <motion.div key={item} layoutId={item}>
          {item}
        </motion.div>
      ))}
    </motion.div>
  );
}

The layoutId prop helps Framer Motion understand how elements should transition between different states. Using AnimatePresence, you can animate components when they enter or exit the DOM.

💡 Performance Considerations

Animations can be resource-intensive if not handled properly. Here are some tips to ensure your animations run smoothly:

  • Use transform properties (like x, y, scale) instead of non-transform properties (like top, left).
  • Avoid unnecessary re-renders by using state wisely.
  • Test animations on different devices to ensure they run smoothly.

💡 Accessibility in Motion

Animations can enhance user experience, but they should never come at the cost of accessibility. Here are some best practices:

  • Provide a way to disable animations for users with motion sensitivity.
  • Ensure that all animations have a purpose and do not cause distraction.
  • Use semantic HTML elements whenever possible.

💡 Real-World Applications

Now that you've learned the basics, let's see how Framer Motion can be used in real-world applications. From simple hover effects to complex loading animations, the possibilities are endless!

🎯 Portals, Refs, and Imperative Code

In this chapter, we'll explore three powerful React concepts: Portals, Refs, and Imperative Code. These tools allow you to create more dynamic and interactive user interfaces while maintaining control over your components.

💡 Portals: Render Components Outside the DOM Tree

Portals provide a way to render React components outside of your current DOM hierarchy. This is particularly useful for creating overlays, modals, or tooltips that need to appear above other elements.

import { createPortal } from 'react';

export default function Modal({ children }) {
  return createPortal(
    <div className="modal-overlay">
      <div className="modal-content">{children}</div>
    </div>,
    document.getElementById('portal-root')
  );
}

💡 Key Information About Portals

  • Portals are created using the createPortal function.
  • The portal root must be an element in your DOM tree.
  • Use portals sparingly to avoid complex component hierarchies.

💡 Refs: Imperative Control Over Elements

Refs provide a way to directly access DOM elements or other React components. They are particularly useful when you need to control elements imperatively.

import { useRef, useEffect } from 'react';

export default function Timer() {
  const timerRef = useRef(null);
  
  useEffect(() => {
    const interval = setInterval(timerRef.current.textContent = new Date().toLocaleTimeString();
    return () => clearInterval(interval);
  }, 1000);
  
  return <div ref={timerRef}></div>;
}

💡 Key Information About Refs

  • Use useRef to create a ref in functional components.
  • Refs can be used to access DOM elements or other components.
  • Avoid using refs for things you can do declaratively.

💡 Forwarding Refs and Imperative Handles

When creating custom hooks or components, you may want to forward refs to underlying elements. This can be done using forwardRef and useImperativeHandle.

import { forwardRef, useImperativeHandle } from 'react';

export default function CustomInput({ ...props }, ref) {
  const [value, setValue] = useState('');
  
  useImperativeHandle(ref, () => ({
    setValue,
    getValue: () => value
  }));
  
  return (
    <input
      ref={ref}
      value={value}
      onChange={(e) => setValue(e.target.value)}
    />
  );
}

💡 Key Information About Imperative Code

  • Use forwardRef to pass refs through your components.
  • useImperativeHandle allows you to expose specific methods to the ref.
  • Imperative code should be used sparingly and only when necessary.

Quiz

Question 1 of 10

What is the purpose of the §motion§ component in Framer Motion?

  • To create animations
  • To handle state management
  • To manage layout transitions
  • All of the above