Badge Ping Counter
An icon badge counter that bounces and pings once on every increment.
- Category
- Components
- Added
- 2026-08-24
- Deps
- 2
- Updated
- 2026-09-01
Props
"use client";
import { useState } from "react";
import { AnimatePresence, motion } from "framer-motion";
import { Bell } from "lucide-react";
export function BadgePingCounter({ initialCount = 0 }: { initialCount?: number }) {
const [count, setCount] = useState(initialCount);
return (
<button
onClick={() => setCount((c) => c + 1)}
className="relative flex h-11 w-11 items-center justify-center rounded-full border border-neutral-200 text-neutral-500"
>
<Bell size={17} />
{count > 0 && (
<span className="absolute -right-1 -top-1 flex h-5 min-w-5 items-center justify-center rounded-full bg-red-500 px-1">
<AnimatePresence mode="popLayout">
<motion.span
key={count}
initial={{ scale: 0.4, opacity: 0 }}
animate={{ scale: 1, opacity: 1 }}
exit={{ scale: 0.4, opacity: 0 }}
transition={{ type: "spring", stiffness: 500, damping: 22 }}
className="text-[10px] font-bold text-white"
>
{count}
</motion.span>
</AnimatePresence>
<motion.span
key={`ring-${count}`}
initial={{ scale: 1, opacity: 0.6 }}
animate={{ scale: 2.2, opacity: 0 }}
transition={{ duration: 0.5, ease: "easeOut" }}
className="pointer-events-none absolute inset-0 rounded-full bg-red-500"
/>
</span>
)}
</button>
);
}
About this component
A bell icon with a badge number is easy; making an increment actually feel like something happened is the harder part. This component layers two independent Framer Motion animations on every count change: the number itself bounces in via AnimatePresence keyed on the count value, and a separate expanding ring, keyed as ring-${count} so it remounts fresh on every single increment, scales from 1 to 2.2 while fading to nothing behind it. That per-increment key is the part worth noticing; a fixed key on the ring would only ever animate once, on first mount, since React has no reason to remount an element whose key never changes. Reach for this over the plainer Animated Counter Badge when the ping itself is the point, a real-time notification feed where each new item should register as an event, not just a number tracking state. For anything incrementing faster than a person can read, like a live view counter, the ring just looks like flicker. It is a click-to-increment demo in the shipped code; a real integration replaces that with a websocket or polling update.
Curator’s note
The ping ring is keyed by `ring-${count}`, not a fixed key — a fixed key would only ever play the ring animation on the very first mount, since React has nothing to remount on subsequent increments without a key change.
Pairs well with
Matched on shared tags and category — the entries most likely to be used alongside this one.
Announcement Badge Hero
heroesA large pulsing announcement pill anchors the hero above one headline.
Peeking Toast Stack
patternsToasts collapsed into a depth-scaled deck that fans out on hover.
Rotating Seal Badge
componentsA round seal — the phrase fitted exactly around the circle with drawn dot separators — that turns once every 24 seconds, eases to a stop on hover, and is the link.