DEV Community

Fazal Shah
Fazal Shah

Posted on

How to Use Lottie Animations in Vue.js (2025 Guide)

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-lottie exposes 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 → .lottie format (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
Enter fullscreen mode Exit fullscreen mode

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')
Enter fullscreen mode Exit fullscreen mode

Local registration

<script setup>
import { Vue3Lottie } from 'vue3-lottie'
</script>
Enter fullscreen mode Exit fullscreen mode

Step 3: Basic Usage

<script setup>
import LoadingAnimation from '@/assets/loading.json'
</script>

<template>
  <Vue3Lottie
    :animationData="LoadingAnimation"
    :height="200"
    :width="200"
  />
</template>
Enter fullscreen mode Exit fullscreen mode

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>
Enter fullscreen mode Exit fullscreen mode

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>
Enter fullscreen mode Exit fullscreen mode

Speed Control

<template>
  <!-- Half speed -->
  <Vue3Lottie :animationData="anim" :speed="0.5" />

  <!-- Double speed -->
  <Vue3Lottie :animationData="anim" :speed="2" />
</template>
Enter fullscreen mode Exit fullscreen mode

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>
Enter fullscreen mode Exit fullscreen mode

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>
Enter fullscreen mode Exit fullscreen mode

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>
Enter fullscreen mode Exit fullscreen mode

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
Enter fullscreen mode Exit fullscreen mode
<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>
Enter fullscreen mode Exit fullscreen mode

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>
Enter fullscreen mode Exit fullscreen mode

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>
Enter fullscreen mode Exit fullscreen mode

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>
Enter fullscreen mode Exit fullscreen mode

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>
Enter fullscreen mode Exit fullscreen mode

Common Issues

Animation not showing

  • Check that animationData is 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

  1. Install vue3-lottie (Vue 3) or @lottiefiles/dotlottie-vue for .lottie files
  2. Import your animation JSON and pass it via :animationData
  3. Use :loop, :speed, :autoPlay props for basic control
  4. Use ref + lottieRef.value.play() / .pause() for programmatic control
  5. Listen to @onComplete to chain animations or trigger UI state
  6. Convert to .lottie at IconKing for smaller bundle size
  7. Pause off-screen animations with IntersectionObserver

Top comments (0)