Lottie animations work naturally with Alpine.js — here's a complete integration guide with x-data, x-init, and interactive controls.
Why Alpine.js + Lottie?
Alpine.js and Lottie are a natural pairing: both are lightweight, both work without a build step, and both excel at progressive enhancement. If you're building a page with Alpine.js and want animations that look like they cost $50k to produce, Lottie delivers them in a 10KB file.
Before touching any code, preview and edit your animation at IconKing — it lets you change colors and convert .json to .lottie without any account.
Setup (No Build Step)
<!-- Alpine.js CDN -->
<script src="https://unpkg.com/alpinejs@3" defer></script>
<!-- lottie-web CDN -->
<script src="https://cdnjs.cloudflare.com/ajax/libs/lottie-web/5.12.2/lottie.min.js"></script>
That's all the setup you need.
Basic Integration with x-init
<div
x-data="{ anim: null }"
x-init="
anim = lottie.loadAnimation({
container: $el,
renderer: 'svg',
loop: true,
autoplay: true,
path: '/animations/hero.json'
})
"
style="width: 200px; height: 200px;"
></div>
x-init fires after Alpine initializes the component. $el refers to the root element — no need for a separate document.getElementById.
Interactive Controls
<div x-data="{
anim: null,
playing: true,
init() {
this.anim = lottie.loadAnimation({
container: this.$refs.lottieContainer,
renderer: 'svg',
loop: true,
autoplay: true,
path: '/animations/loader.json'
});
},
toggle() {
this.playing ? this.anim.pause() : this.anim.play();
this.playing = !this.playing;
}
}">
<div x-ref="lottieContainer" style="width: 150px; height: 150px;"></div>
<button @click="toggle()" x-text="playing ? 'Pause' : 'Play'"></button>
</div>
Key pattern: use x-ref instead of $el when the container is a child element rather than the root.
Hover-Triggered Animation
<div x-data="{
anim: null,
init() {
this.anim = lottie.loadAnimation({
container: this.$refs.icon,
renderer: 'svg',
loop: false,
autoplay: false,
path: '/animations/hover-icon.json'
});
}
}"
@mouseenter="anim.play()"
@mouseleave="anim.stop()"
style="display: inline-block; cursor: pointer;">
<div x-ref="icon" style="width: 40px; height: 40px;"></div>
</div>
Loading State Pattern
A common pattern is showing a Lottie loader while data fetches:
<div x-data="{
loading: true,
data: null,
lottieAnim: null,
async init() {
this.lottieAnim = lottie.loadAnimation({
container: this.$refs.loader,
renderer: 'svg',
loop: true,
autoplay: true,
path: '/animations/loading.json'
});
this.data = await fetch('/api/data').then(r => r.json());
this.loading = false;
this.lottieAnim.destroy();
}
}">
<div x-show="loading" x-ref="loader" style="width: 80px; height: 80px;"></div>
<div x-show="!loading">
<p x-text="data?.message"></p>
</div>
</div>
Using dotLottie Format
The .lottie format is ~75% smaller. To use it with Alpine.js, swap lottie-web for dotlottie-web:
<script type="module">
import { DotLottie } from 'https://esm.sh/@lottiefiles/dotlottie-web';
document.addEventListener('alpine:init', () => {
Alpine.data('lottiePlayer', () => ({
dotLottie: null,
init() {
this.dotLottie = new DotLottie({
canvas: this.$refs.canvas,
src: '/animations/hero.lottie',
loop: true,
autoplay: true,
});
},
destroy() {
this.dotLottie?.destroy();
}
}));
});
</script>
<div x-data="lottiePlayer" x-init="init()" @alpine:destroyed="destroy()">
<canvas x-ref="canvas" width="200" height="200"></canvas>
</div>
Note: dotlottie-web requires a <canvas> element, not a <div>.
Cleanup on Component Destroy
Alpine doesn't automatically clean up Lottie instances. Use the destroy magic event:
<div x-data="{ anim: null }"
x-init="anim = lottie.loadAnimation({ container: $el, loop: true, autoplay: true, path: '/anim.json', renderer: 'svg' })"
@alpine:destroyed="anim?.destroy()">
</div>
Without this, animations keep running in memory even after the element is removed from the DOM.
Workflow
- Get .json from LottieFiles or your designer
- Preview and edit colors at IconKing
- Convert to .lottie at IconKing for smaller files
- Add lottie-web via CDN (or dotlottie-web for .lottie format)
- Wire up with
x-initandx-ref
Alpine.js + Lottie gives you polished animations without a build pipeline. Start with a loading spinner and go from there.
Top comments (0)