/SKILL.md
Implement Lottie animations using dotLottie runtimes (@lottiefiles/dotlottie-web and @lottiefiles/dotlottie-react). Use when building, debugging, or optimizing dotLottie or Lottie animations in web projects, including vanilla JS, React, and Next.js. Covers package selection, Web Workers, state machines, theming, dynamic slot overriding, performance best practices, and common patterns.
npx skillsauth add lottiefiles/dotlottie-web dotlottie-webInstall this skill globally with one command. Works with Claude Code, Cursor, and Windsurf.
3 of 9 scanners reported clean
Some scanners were skipped, did not run, or reported a non-clean status. Review each row below.
You are an expert at implementing Lottie animations using dotLottie runtimes. Follow these guidelines when working with dotLottie in web projects.
@lottiefiles/dotlottie-web when:@lottiefiles/dotlottie-react when:# Web (vanilla JS, Vue, Svelte, etc.)
npm install @lottiefiles/dotlottie-web
# React
npm install @lottiefiles/dotlottie-react
import { DotLottie } from '@lottiefiles/dotlottie-web';
const dotLottie = new DotLottie({
canvas: document.getElementById('canvas') as HTMLCanvasElement,
src: 'https://example.com/animation.lottie',
autoplay: true,
loop: true,
});
import { DotLottieReact } from '@lottiefiles/dotlottie-react';
function Animation() {
return (
<DotLottieReact
src="https://example.com/animation.lottie"
autoplay
loop
/>
);
}
import { useRef } from 'react';
import { DotLottieReact } from '@lottiefiles/dotlottie-react';
import type { DotLottie } from '@lottiefiles/dotlottie-web';
function Animation() {
const dotLottieRef = useRef<DotLottie | null>(null);
return (
<DotLottieReact
src="https://example.com/animation.lottie"
dotLottieRefCallback={(dotLottie) => (dotLottieRef.current = dotLottie)}
/>
);
}
Always prefer .lottie format over .json:
Use DotLottieWorker to offload animation rendering to a Web Worker, keeping the main thread free for UI interactions:
import { DotLottieWorker } from '@lottiefiles/dotlottie-web';
const dotLottie = new DotLottieWorker({
canvas: document.getElementById('canvas') as HTMLCanvasElement,
src: 'https://example.com/animation.lottie',
autoplay: true,
loop: true,
});
By default, all DotLottieWorker instances share the same worker. Group animations into separate workers using workerId:
// Hero animation in its own worker
const heroAnimation = new DotLottieWorker({
canvas: heroCanvas,
src: 'hero.lottie',
workerId: 'hero-worker',
});
// UI animations share a different worker
const buttonAnimation = new DotLottieWorker({
canvas: buttonCanvas,
src: 'button.lottie',
workerId: 'ui-worker',
});
Use DotLottieWorker for:
Use regular DotLottie for:
import { DotLottieWorkerReact } from '@lottiefiles/dotlottie-react';
function Animation() {
return (
<DotLottieWorkerReact
src="animation.lottie"
autoplay
loop
workerId="my-worker" // Optional: dedicate to specific worker
/>
);
}
State machines enable interactive animations without code. See the State Machine Guide for details.
const dotLottie = new DotLottie({
canvas,
src: 'interactive.lottie', // Contains state machine
autoplay: true,
});
// Fire events to trigger state transitions
dotLottie.stateMachineFireEvent('click');
dotLottie.stateMachineFireEvent('hover');
dotLottie.stateMachineFireEvent('custom-event');
// Set numeric/boolean/string inputs for state conditions
// See: https://github.com/LottieFiles/dotlottie-web/wiki/dotLottie-State-Machine-Guide#working-with-inputs
dotLottie.stateMachineSetNumericInput('progress', 0.5);
dotLottie.stateMachineSetBooleanInput('isActive', true);
dotLottie.stateMachineSetStringInput('mode', 'dark');
click - User click/taphover - Mouse enterunhover - Mouse leavecomplete - Animation finishedSlots allow runtime color/value customization. Themes follow the dotLottie 2.0 spec.
const dotLottie = new DotLottie({
canvas,
src: 'themed.lottie',
themeId: 'dark-mode', // Use embedded theme by ID
});
// Or apply theme data directly (JSON string per dotLottie 2.0 spec)
// See: https://dotlottie.io/spec/2.0/#themes
dotLottie.setThemeData(JSON.stringify({
rules: [
{ id: 'primary-color', value: [1, 0.34, 0.13] }, // RGB values 0-1
]
}));
Slots enable runtime customization of animated properties using typed APIs. Available slot types: color, scalar, vector, gradient, text, image.
Key APIs: getSlotIds(), getSlotType(), setColorSlot(), setScalarSlot(),
setVectorSlot(), setGradientSlot(), setTextSlot(), resetSlot(), clearSlots().
For complete API reference with code examples for each slot type, animated keyframes, resetting, bulk updates, common use cases (branding, dark mode, progress indicators), and React integration, see Dynamic Slots Reference.
// Play frames 0-60
dotLottie.setSegment(0, 60);
dotLottie.play();
// Play by marker name (defined in animation)
dotLottie.setMarker('intro');
dotLottie.play();
const markers = dotLottie.markers();
// Returns: [{ name: 'intro', time: 0, duration: 60 }, ...]
dotLottie.addEventListener('load', () => {
console.log('Animation loaded');
});
dotLottie.addEventListener('play', () => {
console.log('Playing');
});
dotLottie.addEventListener('complete', () => {
console.log('Animation completed');
});
dotLottie.addEventListener('frame', ({ currentFrame }) => {
console.log('Frame:', currentFrame);
});
// Clean up
dotLottie.removeEventListener('load', handler);
import { DotLottieWorker } from '@lottiefiles/dotlottie-web';
// Offload rendering to worker thread
const dotLottie = new DotLottieWorker({
canvas,
src: 'complex-animation.lottie',
});
// Only load when visible
const observer = new IntersectionObserver((entries) => {
entries.forEach(entry => {
if (entry.isIntersecting) {
loadAnimation();
observer.disconnect();
}
});
});
observer.observe(container);
DotLottie automatically freezes animations when they're not visible (offscreen). To disable this behavior:
const dotLottie = new DotLottie({
canvas,
src: 'animation.lottie',
renderConfig: {
freezeOnOffscreen: false, // Disable auto-freeze (not recommended)
},
});
By default, devicePixelRatio is set to 75% of the actual value for better performance. For full retina quality (with higher performance cost):
const dotLottie = new DotLottie({
canvas,
src: 'animation.lottie',
renderConfig: {
devicePixelRatio: window.devicePixelRatio, // Full retina (higher CPU/GPU)
},
});
// Always destroy when done (vanilla JS)
dotLottie.destroy();
Note: DotLottieReact handles cleanup automatically on unmount - no manual cleanup needed.
const dotLottie = new DotLottie({
canvas,
src: 'animation.lottie',
useFrameInterpolation: true, // Smooth playback (default)
// useFrameInterpolation: false, // Match original AE frame rate
});
A single .lottie file can contain multiple animations:
// Load specific animation by ID
dotLottie.loadAnimation('animation-2');
// Get all animation IDs
const animations = dotLottie.manifest?.animations;
// Returns: [{ id: 'animation-1' }, { id: 'animation-2' }]
Set canvas size via CSS styles (recommended). DotLottie will automatically determine the optimal drawing area:
<canvas id="canvas" style="width: 400px; height: 400px;"></canvas>
Use the autoResize render config to automatically resize when the container changes:
const dotLottie = new DotLottie({
canvas,
src: 'animation.lottie',
renderConfig: {
autoResize: true, // Canvas resizes to fit container
},
});
canvas.addEventListener('mouseenter', () => dotLottie.play());
canvas.addEventListener('mouseleave', () => dotLottie.pause());
canvas.addEventListener('click', () => {
dotLottie.setFrame(0);
dotLottie.setLoop(false);
dotLottie.play();
});
window.addEventListener('scroll', () => {
const progress = window.scrollY / (document.body.scrollHeight - window.innerHeight);
const frame = progress * dotLottie.totalFrames;
dotLottie.setFrame(frame);
});
function Animation() {
const [isLoaded, setIsLoaded] = useState(false);
return (
<>
{!isLoaded && <Skeleton />}
<DotLottieReact
src="animation.lottie"
style={{ opacity: isLoaded ? 1 : 0 }}
dotLottieRefCallback={(dotLottie) => {
dotLottie.addEventListener('load', () => setIsLoaded(true));
}}
/>
</>
);
}
function ResponsiveAnimation() {
return (
<DotLottieReact
src="animation.lottie"
autoplay
loop
style={{ width: '100%', maxWidth: '400px' }}
renderConfig={{ autoResize: true }}
/>
);
}
// Check if loaded
console.log('Loaded:', dotLottie.isLoaded);
// Get animation info
console.log('Duration:', dotLottie.duration);
console.log('Total Frames:', dotLottie.totalFrames);
console.log('Current Frame:', dotLottie.currentFrame);
console.log('Is Playing:', dotLottie.isPlaying);
console.log('Loop:', dotLottie.loop);
console.log('Speed:', dotLottie.speed);
// Get manifest (for .lottie files)
console.log('Manifest:', dotLottie.manifest);
dotLottie.addEventListener('loadError', (error) => {
console.error('Failed to load animation:', error);
// Show fallback UI
});
dotLottie requires browser APIs. For SSR frameworks:
import dynamic from 'next/dynamic';
const DotLottieReact = dynamic(
() => import('@lottiefiles/dotlottie-react').then(mod => mod.DotLottieReact),
{ ssr: false }
);
function Animation() {
return <DotLottieReact src="animation.lottie" autoplay loop />;
}
development
Maintainer-only workflow for handling GitHub Secret Scanning alerts on OpenClaw. Use when Codex needs to triage, redact, clean up, and resolve secret leakage found in issue comments, issue bodies, PR comments, or other GitHub content.
development
Maintainer workflow for OpenClaw releases, prereleases, changelog release notes, and publish validation. Use when Codex needs to prepare or verify stable or beta release steps, align version naming, assemble release notes, check release auth requirements, or validate publish-time commands and artifacts.
development
Run, watch, debug, and extend OpenClaw QA testing with qa-lab and qa-channel. Use when Codex needs to execute the repo-backed QA suite, inspect live QA artifacts, debug failing scenarios, add new QA scenarios, or explain the OpenClaw QA workflow. Prefer the live OpenAI lane with regular openai/gpt-5.4 in fast mode; do not use gpt-5.4-pro or gpt-5.4-mini unless the user explicitly overrides that policy.
development
End-to-end Parallels smoke, upgrade, and rerun workflow for OpenClaw across macOS, Windows, and Linux guests. Use when Codex needs to run, rerun, debug, or interpret VM-based install, onboarding, gateway smoke tests, latest-release-to-main upgrade checks, fresh snapshot retests, or optional Discord roundtrip verification under Parallels.