| name | creator-plugin-development |
| description | This skill should be used when developing LottieFiles Creator Plugins with the Creator Plugin API.
It applies when (1) creating a new Creator Plugin from scratch, (2) working on plugin code in
plugin.ts, ui.html, plugin/ or src/ directories, (3) using the creator global API for scene
manipulation, (4) building plugin UI (HTML or React) that communicates with a plugin sandbox,
or (5) the user mentions "Creator plugin", "LottieFiles plugin", "creator API", or works with
files like plugin.ts, ui.html, or manifest.json in a plugin project context. Triggers on requests
like "create a plugin", "add a feature to my plugin", "import assets into Creator", or "animate
a layer".
|
Creator Plugin Development
Creator Plugins extend the LottieFiles Creator animation application. They have a two-part sandboxed architecture:
- Plugin Sandbox (
plugin.ts) — Runs in isolation with access to the creator global API. Can manipulate scenes, layers, shapes, keyframes. Cannot make network requests.
- UI (
ui.html or src/) — Rendered in an iframe. Can be plain HTML/JS or a React app. Can make network requests via fetch. Cannot access the creator API.
The two parts communicate exclusively via message passing.
Project Structure
There are two plugin patterns. Identify which one you're working with:
HTML Plugin (simple — most examples use this)
my-plugin/
├── manifest.json # Plugin metadata (id, name, apiVersion, entry, ui)
├── plugin.ts # Sandbox code — has `creator` API access
├── plugin.js # Compiled output (generated by tsc)
├── ui.html # UI — plain HTML/CSS/JS in a single file
└── tsconfig.json # TypeScript config
React Plugin (bundled — for complex UIs)
my-plugin/
├── plugin/
│ ├── manifest.json # Plugin metadata
│ ├── plugin.ts # Sandbox code — has `creator` API access
│ └── [helpers].ts # Optional helper modules
├── src/
│ ├── main.tsx # React DOM entry point
│ ├── app.tsx # Main UI component
│ └── components/ # React components
├── vite.config.ts # Uses @lottiefiles/vite-plugin-creator
├── tsconfig.json # Root config with references
├── tsconfig.plugin.json # Plugin sandbox TypeScript config (no DOM)
├── tsconfig.app.json # UI TypeScript config (DOM + JSX)
├── index.html # Vite app template
└── package.json
The plugin manifest defines the plugin's identity and entry points:
{
"id": "unique-uuid-v4",
"name": "My Plugin",
"apiVersion": "1",
"entry": "plugin.js",
"ui": "ui.html"
}
Development Commands
HTML plugins
npx tsc
After compiling, load in Creator: Plugins > Develop > New plugin > select the plugin directory.
React plugins
npm install
npm run dev
npm run build
npx tsc -b
To load in Creator: Plugins > Develop > New plugin > enter the localhost URL from npm run dev.
Communication Pattern (Critical)
This is the most common source of bugs. The message wrapping is asymmetric — and it works the same for both HTML and React plugins.
UI to Plugin
parent.postMessage(
{ pluginMessage: { type: 'create-shape', color: '#ff0000' } },
'*'
);
Plugin Receives Message
creator.ui.onMessage((msg) => {
if (msg.type === 'create-shape') {
}
});
Plugin to UI
creator.ui.postMessage({ type: 'shape-created', layerId: layer.id });
UI Receives Message
window.addEventListener('message', (event) => {
const message = event.data.pluginMessage;
if (message?.type === 'shape-created') {
}
});
Type-Safe Messages (React plugins)
Define shared message types to catch mismatches at compile time:
export type PluginMessage =
| { type: 'create-shape'; color: string }
| { type: 'import-svg'; content: string }
| { type: 'delete-selection' };
For HTML plugins, define an interface in plugin.ts for the same purpose.
For request/response tracking, include a messageId field.
Key API Patterns
Initialize Plugin
creator.ui.show({ width: 300, height: 500 });
Scene Access
const scene = creator.activeScene;
scene.size;
scene.duration;
scene.framerate;
scene.layers;
Create Shapes
const layer = creator.activeScene.createShapeLayer();
const rect = layer.createRectangle({ size: { width: 200, height: 150 } });
layer.createFill({ type: 'SOLID', color: { r: 66, g: 133, b: 244 } });
Import Assets
const anim = await scene.import({ type: 'LOTTIE', url: 'https://...' });
const img = await scene.import({ type: 'IMAGE', url: 'https://...' });
const svg = await scene.import({ type: 'SVG', url: 'https://...' });
const svgLayer = await scene.import({ type: 'SVG', content: svgString });
LOTTIE and SVG imports return SceneLayer. IMAGE imports return ImageLayer.
Animate Properties
layer.position.addKeyframes([
{ frame: 0, value: { x: 100, y: 100 } },
{ frame: 60, value: { x: 400, y: 100 } },
]);
const easeInOut = { type: 'CUBIC_BEZIER', x1: 0.42, y1: 0, x2: 0.58, y2: 1 };
layer.position.addKeyframes([
{ frame: 0, value: { x: 50, y: 100 }, easing: easeInOut },
{ frame: 60, value: { x: 350, y: 100 } },
]);
Selection
const selectedNodes = creator.selection.nodes;
creator.on('selection:nodes', (nodes) => {
creator.ui.postMessage({ type: 'selection-changed', count: nodes.length });
});
Node Type Checking
Always verify node types before operations:
const layers = creator.selection.nodes;
layers.forEach((node) => {
if (node.type === 'SHAPE_LAYER') {
} else if (node.type === 'IMAGE_LAYER') {
} else if (node.type === 'SCENE_LAYER') {
} else if (node.type === 'TEXT_LAYER') {
}
});
Network Requests
The plugin sandbox cannot make fetch requests. Use this pattern:
- UI fetches data from external API (in
ui.html script or React component)
- UI sends data to plugin via
parent.postMessage({ pluginMessage: ... }, '*')
- Plugin processes data and applies to scene
For complete examples, see references/network-and-libraries.md.
Common Pitfalls
- Missing
pluginMessage wrapper — UI-to-plugin messages MUST be wrapped: { pluginMessage: { ... } }. Plugin-to-UI messages do NOT need wrapping.
- Fetching from plugin sandbox — Network requests only work in UI code. Move
fetch calls to ui.html or src/.
- Using
localStorage/sessionStorage — The sandboxed iframe blocks browser storage APIs. Use creator.clientStorage from plugin code instead.
- Not checking node types — Always verify
node.type before accessing type-specific properties.
- Setting
staticValue on animated properties — Setting staticValue when keyframes exist will not affect the animation. Clear keyframes first or modify keyframe values directly.
- Invisible shapes — Shapes need a fill or stroke to be visible. After
createRectangle(), call createFill().
- Scale values are percentages —
100 = 100% scale (not 1.0). Use { x: 100, y: 100 } for normal size.
- Opacity is 0-100 — Not 0-1. Use
100 for fully opaque.
- Color values are 0-255 — RGB channels use the range
{ r: 0-255, g: 0-255, b: 0-255 }.
- Not calling
creator.ui.show() early — Call it at the top of plugin.ts, before setting up message handlers.
- Forgetting to recompile HTML plugins — After editing
plugin.ts, run npx tsc to regenerate plugin.js. React plugins auto-rebuild with npm run dev.
Verification Checklist
Before considering a task complete:
Reference Guide
For deeper information, consult these reference files as needed:
| Reference | When to Consult |
|---|
references/architecture-and-communication.md | Detailed architecture, complete message passing examples, UI API |
references/scene-graph-and-nodes.md | Scene hierarchy, node types, traversal patterns |
references/shapes-styling-animation.md | Creating shapes, fills/strokes/gradients, keyframes, easing |
references/importing-assets.md | LOTTIE/SVG/IMAGE import formats and patterns |
references/storage-and-events.md | clientStorage, node data, selection events, timeline API |
references/network-and-libraries.md | Fetch-from-UI pattern, using npm packages and CDN libraries |