Skip to content
All Insights
Jan 202610 min read

Why We Use Framer Motion for Production Animations

How SLIIQQUE leverages Framer Motion to create micro-interactions, page transitions, and gesture-based animations that delight users.

Gabriel Njoabozia
Gabriel NjoaboziaFounder & Lead Engineer
Framer Motion animation sequences showing micro-interactions, gesture UI, and page transitions

Animation as a Design Discipline

At SLIIQQUE, we believe animation is not decoration, it's communication. A well-timed transition tells the user where content came from and where it's going. A subtle micro-interaction confirms an action without requiring a notification. A scroll-triggered parallax creates a sense of depth and narrative. Framer Motion is the tool we trust to deliver all of this in production. The studio's own site is built on it, and we've shipped it for clients ranging from fintech dashboards to creative portfolios.

Why Framer Motion?

There are many animation libraries for React like react-spring, GSAP, and CSS transitions, but Framer Motion strikes the ideal balance between power and developer experience. Its declarative API integrates naturally with React's component model. Spring physics produce natural-feeling motion without manual easing curves. And features like AnimatePresence, layout animations, and gesture handling build on each other without friction.

Micro-Interactions That Delight

The smallest animations often have the biggest impact on perceived quality. A button that depresses on click, a toggle that slides with a spring, a card that lifts on hover. These signals make the interface feel responsive and crafted.

// A button with spring-powered hover and tap feedback
<motion.button
  whileHover={{ scale: 1.02 }}
  whileTap={{ scale: 0.97 }}
  transition={{
    type: "spring",
    stiffness: 400,
    damping: 17,
  }}
  className="px-6 py-3 bg-orange-500 text-white rounded-lg"
>
  Get Started
</motion.button>

Spring-based motion feels alive because it mimics natural physics. Unlike CSS cubic-bezier curves, springs respond dynamically: the duration adjusts based on the distance traveled. A small hover lift and a large modal entrance both feel consistent because the same spring parameters drive them.

AnimatePresence for Mount/Unmount Transitions

One of React's longstanding challenges is animating elements as they leave the DOM. AnimatePresence solves this elegantly by keeping the exiting element in the tree until its exit animation completes.

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

function NotificationStack({ notifications }) {
  return (
    <div className="fixed top-4 right-4 space-y-2">
      <AnimatePresence>
        {notifications.map((n) => (
          <motion.div
            key={n.id}
            initial={{ opacity: 0, x: 100, scale: 0.9 }}
            animate={{ opacity: 1, x: 0, scale: 1 }}
            exit={{ opacity: 0, x: 100, scale: 0.9 }}
            transition={{ type: "spring", stiffness: 300, damping: 25 }}
            className="bg-white/10 backdrop-blur-xl px-4 py-3 rounded-lg"
          >
            {n.message}
          </motion.div>
        ))}
      </AnimatePresence>
    </div>
  )
}

The key prop is essential here. AnimatePresence uses it to track which elements are entering, exiting, or persisting. Every notification gets smooth enter and exit animations, and stacking new ones pushes existing items naturally.

Layout Animations for Smooth Repositioning

When list items reorder, or when a component changes size, the sudden jump is jarring. Framer Motion's layout prop automatically animates position and size changes by detecting the DOM rect delta.

// Items animate to new positions when the list is filtered or reordered
function SortableList({ items }: { items: Item[] }) {
  const [sortOrder, setSortOrder] = useState<"asc" | "desc">("asc")
  
  const sorted = [...items].sort((a, b) =>
    sortOrder === "asc" ? a.name.localeCompare(b.name) : b.name.localeCompare(a.name)
  )
  
  return (
    <div className="space-y-2">
      {sorted.map((item) => (
        <motion.div
          key={item.id}
          layout
          transition={{ type: "spring", stiffness: 300, damping: 30 }}
          className="p-4 bg-white/5 rounded-lg"
        >
          {item.name}
        </motion.div>
      ))}
    </div>
  )
}

This is one of Framer Motion's most powerful features. Layout animations handle the hard part, calculating start and end positions, and animate the transition. We used this pattern in a client's Kanban board to create smooth card movement between columns.

Scroll-Triggered Animations with useInView

Scroll-based reveals create narrative pacing. Elements fade, slide, or scale in as they enter the viewport. Framer Motion's useInView hook makes this trivial without Intersection Observer boilerplate.

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

function FadeInSection({ children }: { children: React.ReactNode }) {
  const ref = useRef(null)
  const isInView = useInView(ref, { once: true, margin: "-100px" })
  
  return (
    <motion.div
      ref={ref}
      initial={{ opacity: 0, y: 60 }}
      animate={isInView ? { opacity: 1, y: 0 } : { opacity: 0, y: 60 }}
      transition={{ duration: 0.8, ease: "easeOut" }}
    >
      {children}
    </motion.div>
  )
}

// Usage: any section automatically animates in when scrolled to
<FadeInSection>
  <h2>Our Services</h2>
  <p>Content that reveals as you scroll creates a natural reading rhythm.</p>
</FadeInSection>

The once: true option ensures the animation only fires once, avoiding distracting re-animations. The margin property lets you trigger the animation before the element fully enters the viewport, creating a more polished feel.

Stagger Children for Cascading Entrances

When multiple elements enter simultaneously, staggering their animation creates visual hierarchy and reduces cognitive load. Framer Motion's staggerChildren makes this trivial.

const containerVariants = {
  hidden: { opacity: 0 },
  visible: {
    opacity: 1,
    transition: {
      staggerChildren: 0.1,
      delayChildren: 0.2,
    },
  },
}

const itemVariants = {
  hidden: { opacity: 0, y: 20 },
  visible: {
    opacity: 1,
    y: 0,
    transition: { type: "spring", stiffness: 300, damping: 24 },
  },
}

function FeatureGrid({ features }: { features: Feature[] }) {
  return (
    <motion.div
      variants={containerVariants}
      initial="hidden"
      animate="visible"
      className="grid grid-cols-3 gap-6"
    >
      {features.map((f) => (
        <motion.div
          key={f.title}
          variants={itemVariants}
          className="p-6 bg-white/5 rounded-lg"
        >
          <h3>{f.title}</h3>
          <p>{f.description}</p>
        </motion.div>
      ))}
    </motion.div>
  )
}

We use staggerChildren across our own site's section entrances. The consistent rhythm of items appearing every 100ms creates a polished, deliberate feel without overwhelming the user.

Shared Layout Animations

The layoutId prop enables elements to morph between routes or states. When a card expands into a detail view, the shared layoutId creates a seamless transition that grounds the user in the interface.

// Card grid: clicking a card morphs it into the detail view
function CardGrid() {
  const [selectedId, setSelectedId] = useState<string | null>(null)
  
  return (
    <div className="grid grid-cols-3 gap-4">
      {cards.map((card) => (
        <motion.div
          key={card.id}
          layoutId={card.id}
          onClick={() => setSelectedId(card.id)}
          className="p-6 bg-white/5 rounded-lg cursor-pointer"
        >
          <h3>{card.title}</h3>
          {selectedId === card.id && (
            <motion.div
              initial={{ opacity: 0 }}
              animate={{ opacity: 1 }}
              className="mt-4"
            >
              <p>Detailed content with shared layout animation.</p>
            </motion.div>
          )}
        </motion.div>
      ))}
    </div>
  )
}

This pattern is especially effective for image galleries, card-based layouts, and navigation transitions. The user's brain tracks the element across state changes, reducing disorientation and creating a feeling of continuity.

Gesture-Based Interactions

Framer Motion's drag, hover, and tap gestures enable interaction patterns that feel native. A draggable carousel, a swipeable card, or a pull-to-refresh. All are achievable with a few lines of declarative motion markup.

// Swipeable card: drag to dismiss
function SwipeableCard({ children, onDismiss }) {
  return (
    <motion.div
      drag="x"
      dragConstraints={{ left: 0, right: 0 }}
      dragElastic={0.9}
      onDragEnd={(_, info) => {
        if (Math.abs(info.offset.x) > 150) {
          onDismiss()
        }
      }}
      whileDrag={{ scale: 1.05, rotate: 3 }}
      className="p-6 bg-white/5 rounded-lg cursor-grab active:cursor-grabbing"
    >
      {children}
    </motion.div>
  )
}

Gesture-driven interfaces feel more direct and responsive. We've used drag interactions for mobile-first product cards, sortable lists, and interactive onboarding flows.

Performance Considerations

Framer Motion is performant because it uses CSS transforms and opacity for most animations. Those are properties the browser's compositor thread handles without triggering layout or paint. We follow a few rules: prefer transform and opacity over width, height, or top/left; keep animated layers isolated to minimize paint area; and use will-change: transform on animated elements. The result is 60fps animations even on mid-range devices.

Our Animation Philosophy

At SLIIQQUE, every animation must answer: “Does this help the user understand what's happening?” If the answer is no, we remove it. Animation should reduce cognitive friction, not add it. A page transition that explains spatial relationships. A button press that confirms action. A loading skeleton that sets expectation. When animation serves understanding, it earns its place in the bundle.

We build Framer Motion into every project we ship, from micro-interactions to page-wide narrative animations. If your product needs motion that communicates, we'd love to show you what's possible.

Let's animate your product →

Want motion that tells your story? Let's talk →