Angular's component-based architecture and dependency injection make Lottie integration clean and reusable. This guide covers everything from basic setup to lazy loading, services, and RxJS-driven control.
Before You Start
Open your animation files in IconKing first:
- Preview colors, timing, and layers before integrating
- Convert
.jsonâ.lottiefor 75% smaller file size - Edit colors to match your Angular app's design tokens
Installation
npm install lottie-web
npm install --save-dev @types/lottie-web
# Or for dotLottie format
npm install @lottiefiles/dotlottie-web
Basic Component (Vanilla lottie-web)
The most direct approach â create a reusable Angular component:
// src/app/components/lottie-animation/lottie-animation.component.ts
import {
Component,
Input,
OnInit,
OnDestroy,
ElementRef,
ViewChild,
AfterViewInit
} from '@angular/core';
import lottie, { AnimationItem } from 'lottie-web';
@Component({
selector: 'app-lottie-animation',
template: `<div #container [style.width.px]="width" [style.height.px]="height"></div>`,
standalone: true,
})
export class LottieAnimationComponent implements AfterViewInit, OnDestroy {
@ViewChild('container') containerRef!: ElementRef<HTMLDivElement>;
@Input() src = '';
@Input() width = 200;
@Input() height = 200;
@Input() loop = true;
@Input() autoplay = true;
private anim: AnimationItem | null = null;
ngAfterViewInit(): void {
this.anim = lottie.loadAnimation({
container: this.containerRef.nativeElement,
renderer: 'svg',
loop: this.loop,
autoplay: this.autoplay,
path: this.src,
});
}
ngOnDestroy(): void {
this.anim?.destroy();
}
play(): void { this.anim?.play(); }
pause(): void { this.anim?.pause(); }
stop(): void { this.anim?.stop(); }
}
<!-- app.component.html -->
<app-lottie-animation
src="/assets/animations/hero.json"
[width]="400"
[height]="400"
/>
Place animation files in src/assets/animations/ â Angular copies assets/ to the build output.
Using ngx-lottie (Recommended Wrapper)
ngx-lottie is the maintained Angular-specific wrapper:
npm install ngx-lottie lottie-web
App configuration (standalone API):
// app.config.ts
import { ApplicationConfig } from '@angular/core';
import { provideLottieOptions } from 'ngx-lottie';
import player from 'lottie-web';
export const appConfig: ApplicationConfig = {
providers: [
provideLottieOptions({
player: () => player,
}),
],
};
Module-based setup:
// app.module.ts
import { LottieModule } from 'ngx-lottie';
import player from 'lottie-web';
export function playerFactory() {
return player;
}
@NgModule({
imports: [
LottieModule.forRoot({ player: playerFactory }),
],
})
export class AppModule {}
Usage in templates:
<!-- Using object options -->
<ng-lottie
[options]="lottieOptions"
[width]="'300px'"
[height]="'300px'"
(animationCreated)="onAnimationCreated($event)"
/>
import { Component } from '@angular/core';
import { AnimationOptions } from 'ngx-lottie';
import { AnimationItem } from 'lottie-web';
@Component({
selector: 'app-hero',
templateUrl: './hero.component.html',
})
export class HeroComponent {
lottieOptions: AnimationOptions = {
path: '/assets/animations/hero.json',
loop: true,
autoplay: true,
};
private animItem: AnimationItem | null = null;
onAnimationCreated(anim: AnimationItem): void {
this.animItem = anim;
}
play(): void { this.animItem?.play(); }
pause(): void { this.animItem?.pause(); }
}
Lazy Loading with loadAnimation
For large animations, load them on demand using Angular's HTTP client:
import { Component, OnInit, OnDestroy, ElementRef, ViewChild, AfterViewInit } from '@angular/core';
import { HttpClient } from '@angular/common/http';
import lottie, { AnimationItem } from 'lottie-web';
import { Subscription } from 'rxjs';
@Component({
selector: 'app-lazy-lottie',
template: `<div #container [style.width.px]="width" [style.height.px]="height"></div>`,
})
export class LazyLottieComponent implements AfterViewInit, OnDestroy {
@ViewChild('container') containerRef!: ElementRef;
width = 300;
height = 300;
private anim: AnimationItem | null = null;
private sub: Subscription | null = null;
constructor(private http: HttpClient) {}
ngAfterViewInit(): void {
this.sub = this.http
.get('/assets/animations/complex.json')
.subscribe((animData) => {
this.anim = lottie.loadAnimation({
container: this.containerRef.nativeElement,
renderer: 'svg',
loop: true,
autoplay: true,
animationData: animData,
});
});
}
ngOnDestroy(): void {
this.sub?.unsubscribe();
this.anim?.destroy();
}
}
Angular Service for Animation Management
For apps with multiple animations, centralize control in a service:
// src/app/services/lottie.service.ts
import { Injectable } from '@angular/core';
import lottie, { AnimationItem } from 'lottie-web';
@Injectable({ providedIn: 'root' })
export class LottieService {
private animations = new Map<string, AnimationItem>();
register(id: string, anim: AnimationItem): void {
this.animations.set(id, anim);
}
play(id: string): void { this.animations.get(id)?.play(); }
pause(id: string): void { this.animations.get(id)?.pause(); }
stop(id: string): void { this.animations.get(id)?.stop(); }
setSpeed(id: string, speed: number): void {
this.animations.get(id)?.setSpeed(speed);
}
destroy(id: string): void {
this.animations.get(id)?.destroy();
this.animations.delete(id);
}
destroyAll(): void {
this.animations.forEach(anim => anim.destroy());
this.animations.clear();
}
}
// In a component
@Component({ /* ... */ })
export class HeroComponent implements AfterViewInit, OnDestroy {
@ViewChild('container') containerRef!: ElementRef;
constructor(private lottieService: LottieService) {}
ngAfterViewInit(): void {
const anim = lottie.loadAnimation({
container: this.containerRef.nativeElement,
renderer: 'svg',
loop: true,
autoplay: true,
path: '/assets/animations/hero.json',
});
this.lottieService.register('hero', anim);
}
ngOnDestroy(): void {
this.lottieService.destroy('hero');
}
}
IntersectionObserver Directive
Create an Angular directive to pause animations when off-screen:
// src/app/directives/lottie-observe.directive.ts
import {
Directive,
ElementRef,
Input,
OnInit,
OnDestroy
} from '@angular/core';
import { AnimationItem } from 'lottie-web';
@Directive({
selector: '[appLottieObserve]',
standalone: true,
})
export class LottieObserveDirective implements OnInit, OnDestroy {
@Input() animItem: AnimationItem | null = null;
private observer: IntersectionObserver | null = null;
constructor(private el: ElementRef) {}
ngOnInit(): void {
this.observer = new IntersectionObserver(
([entry]) => {
if (entry.isIntersecting) {
this.animItem?.play();
} else {
this.animItem?.pause();
}
},
{ threshold: 0.1 }
);
this.observer.observe(this.el.nativeElement);
}
ngOnDestroy(): void {
this.observer?.disconnect();
}
}
<div
#container
appLottieObserve
[animItem]="animItem"
style="width: 300px; height: 300px"
></div>
prefers-reduced-motion in Angular
import { Injectable } from '@angular/core';
@Injectable({ providedIn: 'root' })
export class MotionService {
private mq = window.matchMedia('(prefers-reduced-motion: reduce)');
get prefersReduced(): boolean {
return this.mq.matches;
}
onChange(callback: (reduced: boolean) => void): void {
this.mq.addEventListener('change', (e) => callback(e.matches));
}
}
// In component
constructor(private motionService: MotionService) {}
ngAfterViewInit(): void {
const reduced = this.motionService.prefersReduced;
this.anim = lottie.loadAnimation({
/* ... */
loop: !reduced,
autoplay: !reduced,
});
if (reduced) {
this.anim.addEventListener('DOMLoaded', () => {
this.anim!.goToAndStop(this.anim!.totalFrames - 1, true);
});
}
}
dotLottie in Angular
For .lottie format (75% smaller files):
npm install @lottiefiles/dotlottie-web
import { Component, AfterViewInit, ViewChild, ElementRef, OnDestroy } from '@angular/core';
import { DotLottie } from '@lottiefiles/dotlottie-web';
@Component({
selector: 'app-dot-lottie',
template: `<canvas #canvas [style.width.px]="width" [style.height.px]="height"></canvas>`,
standalone: true,
})
export class DotLottieComponent implements AfterViewInit, OnDestroy {
@ViewChild('canvas') canvasRef!: ElementRef<HTMLCanvasElement>;
width = 300;
height = 300;
private dotLottie: DotLottie | null = null;
ngAfterViewInit(): void {
this.dotLottie = new DotLottie({
canvas: this.canvasRef.nativeElement,
src: '/assets/animations/hero.lottie',
loop: true,
autoplay: true,
});
}
ngOnDestroy(): void {
this.dotLottie?.destroy();
}
}
Angular Signals Integration (Angular 17+)
Use Angular signals to reactively control animation state:
import { Component, signal, effect, AfterViewInit, ViewChild, ElementRef, OnDestroy } from '@angular/core';
import lottie, { AnimationItem } from 'lottie-web';
@Component({
selector: 'app-reactive-lottie',
template: `
<div #container style="width: 300px; height: 300px"></div>
<button (click)="togglePlay()">
{{ isPlaying() ? 'Pause' : 'Play' }}
</button>
`,
standalone: true,
})
export class ReactiveLottieComponent implements AfterViewInit, OnDestroy {
@ViewChild('container') containerRef!: ElementRef;
isPlaying = signal(true);
speed = signal(1);
private anim: AnimationItem | null = null;
constructor() {
effect(() => {
if (this.anim) {
this.isPlaying() ? this.anim.play() : this.anim.pause();
}
});
effect(() => {
this.anim?.setSpeed(this.speed());
});
}
ngAfterViewInit(): void {
this.anim = lottie.loadAnimation({
container: this.containerRef.nativeElement,
renderer: 'svg',
loop: true,
autoplay: true,
path: '/assets/animations/hero.json',
});
}
togglePlay(): void {
this.isPlaying.set(!this.isPlaying());
}
ngOnDestroy(): void {
this.anim?.destroy();
}
}
Performance Checklist for Angular
| Check | Solution |
|---|---|
Large .json files |
Convert to .lottie at IconKing
|
| Animations in module bundle | Load via HttpClient at runtime |
| Off-screen animations running | Use LottieObserveDirective
|
| Memory leak on route change | Always call anim.destroy() in ngOnDestroy
|
| Multiple simultaneous animations | Use canvas renderer instead of svg
|
Summary
- Use
ngx-lottiefor the cleanest Angular integration with proper typing - Always call
anim.destroy()inngOnDestroyâ route changes cause leaks - For large files, use
HttpClientto fetch at runtime instead of bundling - Create a
LottieServiceto manage multiple animations centrally - Use
IntersectionObserverdirective to pause off-screen animations - Convert to
.lottieat IconKing for smaller files + better Lighthouse scores
Top comments (0)