← Back to blog
·6 min read

How to Animate Lucide Icons in React (Hover, Click and Loop)

TL;DR

  • lucide-react icons are inline SVG with currentColor, so CSS and Motion can animate them directly
  • Hover rings and tilts in CSS, stroke redraws with stroke-dasharray, spring clicks with Motion
  • Loops should stop under prefers-reduced-motion
  • For icons designed for motion, see the animated icons collection

Lucide is the default icon set in a lot of React projects (shadcn/ui uses it), and its icons are static. This guide shows how to animate Lucide icons in React on hover, on click and in a loop, using plain CSS and Motion, without swapping icon libraries.

How lucide-react icons render

Every lucide-react icon renders an inline <svg> with stroke="currentColor" and a class like lucide lucide-bell. That gives you three handles:

  • the className prop, to target the whole icon
  • currentColor, so the icon follows the parent's color
  • inner <path> elements, which you can reach with CSS for stroke effects
  • Hover animations with CSS

    Wrap the icon in a button and animate it from the button, so keyboard focus can share the effect:

    import { Bell } from 'lucide-react'
    
    export function BellButton() {
      return (
        <button aria-label="Notifications" className="group icon-btn">
          <Bell className="bell" size={22} />
        </button>
      )
    }
    .bell { transform-origin: 50% 10%; }
    .icon-btn:hover .bell,
    .icon-btn:focus-visible .bell { animation: ring 600ms ease-in-out; }
    @keyframes ring {
      0%, 100% { transform: rotate(0); }
      20% { transform: rotate(14deg); }
      40% { transform: rotate(-12deg); }
      60% { transform: rotate(8deg); }
      80% { transform: rotate(-4deg); }
    }

    With Tailwind you can do simple ones inline: className="transition-transform duration-200 group-hover:scale-110 group-hover:-rotate-6".

    Redraw the strokes on hover

    Lucide icons are line icons, which makes stroke drawing look great. Lucide paths do not set pathLength, so normalise them in CSS with a large dash value:

    .icon-btn:hover .draw path,
    .icon-btn:hover .draw circle,
    .icon-btn:hover .draw line {
      stroke-dasharray: 60;
      animation: draw 600ms ease-out;
    }
    @keyframes draw {
      from { stroke-dashoffset: 60; }
      to { stroke-dashoffset: 0; }
    }

    60 is longer than most paths in a 24 x 24 Lucide icon. If a long path draws only partly, raise the value. More stroke techniques are in animated SVG icons.

    Click animations with Motion

    Wrap a Lucide icon in motion.span for spring feedback on press:

    import { motion } from 'framer-motion'
    import { Heart } from 'lucide-react'
    import { useState } from 'react'
    
    export function LikeButton() {
      const [liked, setLiked] = useState(false)
      return (
        <button aria-label="Like" aria-pressed={liked} onClick={() => setLiked(!liked)}>
          <motion.span
            style={{ display: 'inline-flex', color: liked ? '#ff3b5c' : 'currentColor' }}
            animate={liked ? { scale: [1, 1.35, 1] } : { scale: 1 }}
            whileTap={{ scale: 0.85 }}
            transition={{ type: 'spring', stiffness: 500, damping: 18 }}
          >
            <Heart fill={liked ? 'currentColor' : 'none'} />
          </motion.span>
        </button>
      )
    }

    Looping animations

    Loaders and live indicators can loop, but should stop for people who prefer reduced motion:

    @media (prefers-reduced-motion: no-preference) {
      .spin { animation: spin 1s linear infinite; }
    }
    @keyframes spin { to { transform: rotate(360deg); } }
    <Loader2 className="spin" aria-hidden="true" />

    Should you use an animated Lucide library?

    Open-source libraries that ship pre-animated Lucide-style icons are a quick way to get consistent hover effects across many icons, and searches for them are rising. They animate the same static shapes, though. When you want icons with real character (squash and stretch, a bell that mutes with a slash drawing in, nav icons that each move differently) you need artwork made for animation.

    Icons made for motion

    The animated icons collection has interactive icons built in Rive: springy hover icons, animated nav icons, a mute bell toggle, emoji reactions and a star rating. Each is a React component with keyboard support that you add by pasting one prompt into Cursor, Claude Code, Lovable or v0, and they sit happily next to your Lucide icons.