Lottie animations are the fastest way to add high-quality motion to a Vue.js app. This guide covers setup, playback control, and integration using vue3-lottie â the most actively maintained Vue 3 Lottie wrapper.
Why Lottie in Vue?
- No GIF overhead â Lottie files are vector-based JSON, typically under 50KB
- Programmatic control â play, pause, loop, seek, speed â all reactive
- Easy theming â change colors at runtime without touching the source file
-
Vue 3 + Composition API friendly â
vue3-lottieexposes a clean ref-based API
Step 0: Preview Your Animation First
Before adding any Lottie file to your project, open it in IconKing:
- See exactly how it looks (colors, timing, layer structure)
- Edit colors to match your design system
- Convert
.jsonâ.lottieformat (75% smaller file size) - Catch broken layers before they surface in your Vue app
No account needed. Drop the file, inspect, adjust, download.
Step 1: Install vue3-lottie
npm install vue3-lottie
vue3-lottie uses lottie-web under the hood and is built for Vue 3 with full TypeScript support.
Step 2: Register the Component
Global registration (recommended for multiple uses)
// main.js
import { createApp } from 'vue'
import App from './App.vue'
import Vue3Lottie from 'vue3-lottie'
createApp(App)
.use(Vue3Lottie)
.mount('#app')
Local registration
<script setup>
import { Vue3Lottie } from 'vue3-lottie'
</script>
Step 3: Basic Usage
<script setup>
import LoadingAnimation from '@/assets/loading.json'
</script>
<template>
<Vue3Lottie
:animationData="LoadingAnimation"
:height="200"
:width="200"
/>
</template>
That's the minimum setup â animationData accepts the imported JSON object directly. The animation plays and loops by default.
Using a URL Instead of a Local File
<template>
<Vue3Lottie
animationLink="https://assets10.lottiefiles.com/packages/lf20_example.json"
:height="200"
:width="200"
/>
</template>
Loop Control
<template>
<!-- Loop forever (default) -->
<Vue3Lottie :animationData="anim" :loop="true" />
<!-- Play once -->
<Vue3Lottie :animationData="anim" :loop="false" />
<!-- Loop 3 times -->
<Vue3Lottie :animationData="anim" :loop="3" />
</template>
Speed Control
<template>
<!-- Half speed -->
<Vue3Lottie :animationData="anim" :speed="0.5" />
<!-- Double speed -->
<Vue3Lottie :animationData="anim" :speed="2" />
</template>
Programmatic Playback with ref
<script setup>
import { ref } from 'vue'
import SuccessAnimation from '@/assets/success.json'
const lottieRef = ref(null)
function play() {
lottieRef.value.play()
}
function pause() {
lottieRef.value.pause()
}
function stop() {
lottieRef.value.stop()
}
</script>
<template>
<Vue3Lottie
ref="lottieRef"
:animationData="SuccessAnimation"
:autoPlay="false"
:loop="false"
/>
<button @click="play">Play</button>
<button @click="pause">Pause</button>
<button @click="stop">Stop</button>
</template>
Listening for Events
<template>
<Vue3Lottie
:animationData="anim"
:loop="false"
@onComplete="handleComplete"
@onLoopComplete="handleLoopComplete"
@onEnterFrame="handleFrame"
/>
</template>
<script setup>
function handleComplete() {
console.log('Animation finished')
}
function handleLoopComplete() {
console.log('One loop completed')
}
function handleFrame(event) {
// event.currentTime â current frame
}
</script>
Play a Specific Segment
<script setup>
import { ref } from 'vue'
import anim from '@/assets/animation.json'
const lottieRef = ref(null)
// Play frames 0â30 only
function playSegment() {
lottieRef.value.playSegments([0, 30], true)
}
</script>
<template>
<Vue3Lottie ref="lottieRef" :animationData="anim" :autoPlay="false" />
<button @click="playSegment">Play intro only</button>
</template>
Using dotLottie (.lottie) Format
.lottie files are ~75% smaller than .json. To use them in Vue 3, switch to @lottiefiles/dotlottie-vue:
npm install @lottiefiles/dotlottie-vue
<script setup>
import { DotLottieVue } from '@lottiefiles/dotlottie-vue'
</script>
<template>
<DotLottieVue
src="/animations/loading.lottie"
:loop="true"
:autoplay="true"
style="width: 200px; height: 200px"
/>
</template>
To convert your .json file to .lottie, use IconKing â drop the file, click convert, download.
Reactive Animation Switching
Swap animations on user action using Vue's reactive data:
<script setup>
import { ref } from 'vue'
import IdleAnim from '@/assets/idle.json'
import SuccessAnim from '@/assets/success.json'
const currentAnim = ref(IdleAnim)
const isSuccess = ref(false)
function triggerSuccess() {
isSuccess.value = true
currentAnim.value = SuccessAnim
}
</script>
<template>
<Vue3Lottie :animationData="currentAnim" :key="isSuccess" :loop="false" />
<button @click="triggerSuccess">Trigger</button>
</template>
The :key binding forces the component to remount when the animation changes â important for resetting state.
Performance Tips
1. Pause when not visible
<script setup>
import { ref, onMounted, onUnmounted } from 'vue'
const lottieRef = ref(null)
const observer = new IntersectionObserver(([entry]) => {
if (lottieRef.value) {
entry.isIntersecting
? lottieRef.value.play()
: lottieRef.value.pause()
}
})
onMounted(() => {
observer.observe(lottieRef.value.$el)
})
onUnmounted(() => {
observer.disconnect()
})
</script>
<template>
<Vue3Lottie ref="lottieRef" :animationData="anim" />
</template>
2. Use .lottie format
Smaller files = faster initial page load. Convert at IconKing.
3. Lazy load animations
<script setup>
import { ref, defineAsyncComponent } from 'vue'
import { Vue3Lottie } from 'vue3-lottie'
const animData = ref(null)
// Only fetch when needed
async function loadAnim() {
const module = await import('@/assets/heavy-animation.json')
animData.value = module.default
}
</script>
<template>
<button @click="loadAnim">Load Animation</button>
<Vue3Lottie v-if="animData" :animationData="animData" />
</template>
Complete Example: Loading State
<script setup>
import { ref } from 'vue'
import { Vue3Lottie } from 'vue3-lottie'
import LoadingAnim from '@/assets/loading.json'
import SuccessAnim from '@/assets/success.json'
const status = ref('idle') // 'idle' | 'loading' | 'success'
async function fetchData() {
status.value = 'loading'
try {
await new Promise(resolve => setTimeout(resolve, 2000)) // simulate API call
status.value = 'success'
} catch {
status.value = 'idle'
}
}
</script>
<template>
<button @click="fetchData" :disabled="status === 'loading'">
Fetch Data
</button>
<Vue3Lottie
v-if="status === 'loading'"
:animationData="LoadingAnim"
:loop="true"
:height="150"
:width="150"
/>
<Vue3Lottie
v-if="status === 'success'"
:animationData="SuccessAnim"
:loop="false"
:height="150"
:width="150"
@onComplete="() => status = 'idle'"
/>
</template>
Common Issues
Animation not showing
- Check that
animationDatais the parsed JSON object, not a string path - If using a URL, check for CORS issues on the server hosting the file
Animation resets unexpectedly
If the parent component re-renders, vue3-lottie may remount. Use :key intentionally and avoid passing unstable references as props.
Wrong colors on screen
Preview in IconKing first. If it looks correct there, the issue is with how the JSON was generated (check your After Effects export settings).
Finding Lottie Files for Vue
- LottieFiles â large free library
- IconKing â preview any file before using it, edit colors to match your Vue app's design system, convert formats
Always preview before committing to a file. What looks good in a desktop browser might clip or scale unexpectedly at mobile breakpoints â catch it in IconKing first.
Summary
- Install
vue3-lottie(Vue 3) or@lottiefiles/dotlottie-vuefor.lottiefiles - Import your animation JSON and pass it via
:animationData - Use
:loop,:speed,:autoPlayprops for basic control - Use
ref+lottieRef.value.play()/.pause()for programmatic control - Listen to
@onCompleteto chain animations or trigger UI state - Convert to
.lottieat IconKing for smaller bundle size - Pause off-screen animations with IntersectionObserver
Top comments (0)