Lottie Hover Icon Button — Free Inline-JSON Lottie Snippet
Lottie Hover Icon Button · Buttons · Plain HTML, CSS & JS · Live preview
Copy & paste
Source Code
<div class="lb-wrap">
<button class="lb-btn" id="lbBtn">
<span class="lb-icon" id="lbIcon"></span>
<span class="lb-label">Add to favorites</span>
</button>
<p class="lb-hint">Hover the button — the icon plays a Lottie animation.</p>
</div>*{box-sizing:border-box;margin:0;padding:0}
body{font-family:system-ui,-apple-system,sans-serif;background:#0b0d16;color:#fff;min-height:100vh;display:flex;align-items:center;justify-content:center}
.lb-wrap{display:flex;flex-direction:column;align-items:center;gap:14px}
.lb-btn{display:flex;align-items:center;gap:10px;padding:12px 22px 12px 16px;border-radius:999px;border:1px solid #2a2f4a;background:linear-gradient(135deg,#181c30,#12121f);color:#fff;font-family:inherit;font-size:14px;font-weight:600;cursor:pointer;transition:border-color .2s,transform .15s}
.lb-btn:hover{border-color:#f472b6}
.lb-btn:active{transform:scale(.97)}
.lb-icon{width:28px;height:28px;display:block;flex-shrink:0}
.lb-hint{font-size:12px;color:#6d7291}// A minimal but genuinely valid Lottie animation defined inline and passed
// straight to animationData: no hosted .json, no network request, nothing that
// can 404. A grey heart sits visible at frame 0 (the resting state); playing
// forward pops it to 120% and fades its fill to pink over 30 frames (1s at
// 30fps). Every keyframe carries its own end value (e) and easing (i / o), which
// lottie-web needs to interpolate. Without them the fill never resolves and the
// icon renders as a black blob.
const ease = { i: { x: 0.3, y: 1 }, o: { x: 0.2, y: 0 } };
const heartAnimation = {
v: '5.9.0', fr: 30, ip: 0, op: 30, w: 100, h: 100, nm: 'heart', ddd: 0,
assets: [],
layers: [{
ddd: 0, ind: 1, ty: 4, nm: 'heart-shape', sr: 1,
ks: {
o: { a: 0, k: 100 },
r: { a: 0, k: 0 },
p: { a: 0, k: [50, 50, 0] },
a: { a: 0, k: [0, 0, 0] },
s: {
a: 1,
k: [
{ t: 0, s: [100, 100, 100], e: [120, 120, 100], ...ease },
{ t: 14, s: [120, 120, 100], e: [100, 100, 100], ...ease },
{ t: 30 },
],
},
},
ao: 0,
shapes: [{
ty: 'gr',
it: [
{
ty: 'sh', ix: 1,
ks: { a: 0, k: {
v: [[0, 34], [-18, -31], [0, -16], [18, -31]],
i: [[39, -26], [-21, 0], [0, -8], [-10, 0]],
o: [[-39, -26], [10, 0], [0, -8], [21, 0]],
c: true,
} },
},
{
ty: 'fl', ix: 2,
c: { a: 1, k: [
{ t: 0, s: [0.43, 0.45, 0.57, 1], e: [0.96, 0.26, 0.57, 1], ...ease },
{ t: 12, s: [0.96, 0.26, 0.57, 1], e: [0.96, 0.26, 0.57, 1], ...ease },
{ t: 30 },
] },
o: { a: 0, k: 100 },
},
{ ty: 'tr', p: { a: 0, k: [0, 0] }, a: { a: 0, k: [0, 0] }, s: { a: 0, k: [100, 100] }, r: { a: 0, k: 0 }, o: { a: 0, k: 100 } },
],
nm: 'heart-group',
}],
ip: 0, op: 30, st: 0, bm: 0,
}],
};
const anim = lottie.loadAnimation({
container: document.getElementById('lbIcon'),
renderer: 'svg',
loop: false,
autoplay: false,
animationData: heartAnimation,
});
const btn = document.getElementById('lbBtn');
btn.addEventListener('mouseenter', () => {
anim.setDirection(1);
anim.play();
});
btn.addEventListener('mouseleave', () => {
anim.setDirection(-1);
anim.play();
});Extend with AI
Take This Snippet Further With AI
The AI prompt for this one is for Patreon supporters ($3 a month, 7 days free), along with a site with no ads. Supporters enter the passcode once and it is remembered in this browser.
What's included
Features
About this UI Snippet
Lottie Hover Icon Button — Play/Reverse on Hover With Inline Animation Data
Lottie plays back animations exported from After Effects (via the Bodymovin plugin) or built directly as JSON, and lottie-web is the runtime that renders that JSON to SVG, canvas, or HTML in the browser. The classic use is a button icon that plays a short flourish on hover — a heart filling in, a bell ringing, a star sparkling — driven by lottie.loadAnimation with autoplay: false and play()/setDirection() calls on mouseenter/mouseleave. This snippet builds exactly that, but since a live sandbox has no server to host a .json asset on, the Lottie data is a small hand-built object passed directly as animationData, so the button always works standalone with zero network requests.
A real, minimal Lottie document
heartAnimation is a genuine Lottie JSON structure — v (format version), fr/ip/op (frame rate and in/out points), and a single shape layer with keyframed s (scale) and a fill c (color) that shift over 30 frames. It's deliberately small (one layer, two keyframed properties) but it's not a mock — lottie.loadAnimation parses and renders it exactly as it would a much larger exported file, which is why this pattern is worth knowing: any time you need a Lottie animation that must never depend on an external asset load, hand-authoring or generating a minimal JSON document and passing it via animationData is the way to guarantee that.
Play forward, reverse back
autoplay: false means nothing happens until you interact. On mouseenter, setDirection(1) then play() runs the animation forward from wherever it currently is; on mouseleave, setDirection(-1) then play() runs it backward to the start. Because Lottie tracks the current frame internally, rapidly hovering on and off reverses smoothly from the interrupted frame rather than snapping or replaying from zero.
Why not autoplay + loop?
A looping hover icon can feel busy on a button that's meant to communicate a single action. This snippet uses loop: false with the play/reverse pattern so the icon settles at a clear "off" state and a clear "hovered" state — a better fit for buttons than for a standalone decorative animation like a loading indicator.
Where this fits
For the equivalent asset-independence pattern applied to a canvas-rendered Rive state machine instead of an SVG Lottie, see Rive interactive icon. For a CSS-only celebratory micro-interaction with no animation library at all, see confetti button or interactive hover button.
Customizing it
Export your own icon animation from After Effects with Bodymovin (or build one in LottieFiles' editor) and either host the resulting .json and pass a path instead of animationData, or inline it the same way this snippet does if you need zero-network guarantees. Swap renderer: 'svg' for 'canvas' if you're animating many icons at once and want lower per-instance overhead.
Step by step
How to Use
- 1Add the lottie-web CDNInclude lottie.min.js from the CDN panel.
- 2Paste HTML, CSS, and JSA button with an empty icon container renders.
- 3Hover the buttonThe heart icon animates in, scaling and changing color.
- 4Move awayThe animation reverses smoothly back to its start frame.
- 5Swap the animation dataReplace heartAnimation with your exported Lottie JSON.
- 6Use a hosted file insteadPass path: 'your.json' in place of animationData.
Real-world uses
Common Use Cases
mouseenter and mouseleave to drive play() calls on a small inline animation.setDirection(-1) reversing smoothly from the current frame.animationData avoids hosted JSON files and extra network requests.Got questions?
Frequently Asked Questions
A live code sandbox has no server to host a .json asset on, and referencing an external URL risks a broken icon if that file ever moves or 404s. Passing a hand-built object directly as animationData sidesteps both problems entirely — lottie-web accepts a JavaScript object just as readily as a fetched JSON file, so the button always renders correctly with zero network dependency.
It's a genuine, if minimal, Lottie JSON document — it has the required top-level fields (v, fr, ip, op, layers) and a shape layer with real keyframed scale and color properties. lottie-web parses and renders it exactly as it would a much larger file exported from After Effects; it's just deliberately small, with one layer and two animated properties, rather than a mock object with fake-looking data.
autoplay is set to false so nothing plays until interaction. On mouseenter, setDirection(1) then play() runs the animation forward from its current frame; on mouseleave, setDirection(-1) then play() runs it backward toward frame 0. Because Lottie tracks the current frame rather than always restarting, moving the mouse on and off quickly reverses smoothly from wherever playback was interrupted instead of jumping or restarting from scratch.
Export a Bodymovin/Lottie JSON from After Effects (or build one in the LottieFiles editor), then either host that .json file and pass path: "your-icon.json" instead of animationData in the loadAnimation call, or — if you need the same zero-network guarantee this snippet has — inline the exported JSON object directly as animationData the same way heartAnimation is defined here.
renderer: "svg" produces crisp vector output at any button size with no raster scaling artifacts, and is the more common default for a single small icon animation. renderer: "canvas" trades that crispness for lower per-instance overhead, which matters more when animating many Lottie icons simultaneously on one page — for a single hover icon like this button, SVG is the better default.