Documentation Index
Fetch the complete documentation index at: https://mintlify.com/shopify/react-native-skia/llms.txt
Use this file to discover all available pages before exploring further.
Skottie is a Lottie animation renderer built on Skia. It allows you to load and render After Effects animations exported via Bodymovin/Lottie, providing full programmatic control over animation properties, colors, text, and more.
Skottie Component
The Skottie component renders Lottie animations on the canvas.
Props
| Name | Type | Description |
|---|
| animation | SkSkottieAnimation | Compiled animation from Skia.Skottie.Make() |
| frame | number | The frame to display (can be animated) |
Basic Example
import { Canvas, Group, Skottie, Skia } from "@shopify/react-native-skia";
const legoAnimationJSON = require("./assets/lego_loader.json");
const animation = Skia.Skottie.Make(JSON.stringify(legoAnimationJSON));
const SkottieExample = () => {
return (
<Canvas style={{ width: 400, height: 300 }}>
<Group transform={[{ scale: 0.5 }]}>
<Skottie animation={animation} frame={41} />
</Group>
</Canvas>
);
};
Creating Animations
Load Lottie JSON data using Skia.Skottie.Make():
import { Skia } from "@shopify/react-native-skia";
const legoAnimationJSON = require("./assets/lego_loader.json");
const animation = Skia.Skottie.Make(
JSON.stringify(legoAnimationJSON)
);
if (!animation) {
throw new Error("Failed to create animation");
}
With External Assets
Many Lottie animations include external assets like fonts and images:
import { Skia } from "@shopify/react-native-skia";
const basicSlotsJSON = require("./assets/basic_slots.json");
const assets = {
"NotoSerif": Skia.Data.fromBytes(fontBytes),
"img_0.png": Skia.Data.fromBytes(imageBytes),
};
const animation = Skia.Skottie.Make(
JSON.stringify(basicSlotsJSON),
assets
);
Animated Playback
Combine Skottie with Reanimated for smooth animation:
import {
Canvas,
Skottie,
useClock,
Group,
Skia,
} from "@shopify/react-native-skia";
import { useDerivedValue } from "react-native-reanimated";
const legoAnimationJSON = require("./assets/lego_loader.json");
const animation = Skia.Skottie.Make(
JSON.stringify(legoAnimationJSON)
);
const AnimatedSkottie = () => {
const clock = useClock();
const frame = useDerivedValue(() => {
const fps = animation.fps();
const duration = animation.duration();
const totalFrames = duration * fps;
const currentFrame = Math.floor((clock.value / 1000) * fps) % totalFrames;
return currentFrame;
});
return (
<Canvas style={{ flex: 1 }}>
<Group transform={[{ scale: 0.5 }]}>
<Skottie animation={animation} frame={frame} />
</Group>
</Canvas>
);
};
Animation Properties
Get information about your animation:
// Duration in seconds
const duration = animation.duration();
// Frames per second
const fps = animation.fps();
// Lottie version
const version = animation.version();
// Animation dimensions
const size = animation.size(); // { width: 800, height: 600 }
Dynamic Properties
Skottie allows runtime modification of animation properties without recreating the animation.
Color Properties
Modify colors programmatically:
import { Skia } from "@shopify/react-native-skia";
const animationJSON = require("./assets/fingerprint.json");
const animation = Skia.Skottie.Make(
JSON.stringify(animationJSON)
);
// Get all color properties
const colorProps = animation.getColorProps();
// Returns: [{ key: string, value: SkColor }, ...]
// Modify a color
if (colorProps.length > 0) {
animation.setColor(
colorProps[0].key,
Skia.Color("rgb(60, 120, 255)")
);
}
Text Properties
Change text content and size:
// Get all text properties
const textProps = animation.getTextProps();
// Returns: [{ key: string, value: { text: string, size: number } }, ...]
// Set text
animation.setText("hello!", "World", 164);
Opacity Properties
Control opacity:
// Get all opacity properties
const opacityProps = animation.getOpacityProps();
// Returns: [{ key: string, value: number }, ...]
// Set opacity (0-100)
animation.setOpacity(opacityProps[0].key, 50);
Modify transforms:
// Get all transform properties
const transformProps = animation.getTransformProps();
// Returns: [{
// key: string,
// value: {
// anchor: { x: number, y: number },
// position: { x: number, y: number },
// scale: { x: number, y: number },
// rotation: number,
// skew: number,
// skewAxis: number
// }
// }, ...]
// Set transform
animation.setTransform(
transformProps[0].key,
{ x: 0, y: 0 }, // anchor
{ x: 100, y: 100 }, // position
{ x: 2, y: 2 }, // scale
45, // rotation
0, // skew
0 // skewAxis
);
Slot Management
Slots are designer-created placeholders for dynamic content replacement.
const slotInfo = animation.getSlotInfo();
// Returns:
// {
// colorSlotIDs: string[],
// imageSlotIDs: string[],
// scalarSlotIDs: string[],
// textSlotIDs: string[],
// vec2SlotIDs: string[]
// }
Setting Slots
Color Slots
import { Skia } from "@shopify/react-native-skia";
const slotInfo = animation.getSlotInfo();
if (slotInfo.colorSlotIDs.length > 0) {
animation.setColorSlot(
slotInfo.colorSlotIDs[0],
Skia.Color("cyan")
);
}
Text Slots
const textSlotProps = {
text: "Hello World",
textSize: 24,
fillColor: Skia.Color("blue"),
};
animation.setTextSlot("TextSlotID", textSlotProps);
Scalar Slots
animation.setScalarSlot("OpacitySlot", 0.5);
Vec2 Slots
animation.setVec2Slot("PositionSlot", { x: 100, y: 200 });
Image Slots
animation.setImageSlot("ImageSlot", "assetName");
Getting Slot Values
const color = animation.getColorSlot("ColorSlotID");
const scalar = animation.getScalarSlot("ScalarSlotID");
const vec2 = animation.getVec2Slot("Vec2SlotID");
const text = animation.getTextSlot("TextSlotID");
Complete Example with Dynamic Properties
import {
Canvas,
Skia,
useClock,
Group,
Skottie
} from "@shopify/react-native-skia";
import { useDerivedValue } from "react-native-reanimated";
const animationJSON = require("./assets/fingerprint.json");
// Create and configure animation
const animation = Skia.Skottie.Make(
JSON.stringify(animationJSON)
);
if (!animation) {
throw new Error("Failed to create animation");
}
// Modify properties
const colorProps = animation.getColorProps();
if (colorProps.length > 0) {
animation.setColor(
colorProps[0].key,
Skia.Color("rgb(60, 120, 255)")
);
}
// Set color slots
const slotInfo = animation.getSlotInfo();
if (slotInfo.colorSlotIDs.length > 0) {
animation.setColorSlot(
slotInfo.colorSlotIDs[0],
Skia.Color("magenta")
);
}
const SkottiePlayer = () => {
const clock = useClock();
const frame = useDerivedValue(() => {
const fps = animation.fps();
const duration = animation.duration();
const totalFrames = duration * fps;
return Math.floor((clock.value / 1000) * fps) % totalFrames;
});
return (
<Canvas style={{ width: 400, height: 400 }}>
<Group transform={[{ scale: 0.5 }]}>
<Skottie animation={animation} frame={frame} />
</Group>
</Canvas>
);
};
Applying Effects
Skottie components don’t follow standard painting rules. Use the layer property:
import {
Canvas,
Skottie,
Skia,
Group,
Paint,
Blur
} from "@shopify/react-native-skia";
const legoAnimationJSON = require("./assets/lego_loader.json");
const animation = Skia.Skottie.Make(
JSON.stringify(legoAnimationJSON)
);
const BlurredSkottie = () => {
return (
<Canvas style={{ flex: 1 }}>
<Group layer={<Paint><Blur blur={10} /></Paint>}>
<Skottie animation={animation} frame={41} />
</Group>
</Canvas>
);
};
With Color Matrix
import { ColorMatrix } from "@shopify/react-native-skia";
<Group
layer={
<Paint>
<ColorMatrix
matrix={[
0, 0, 0, 0, 1,
0, 1, 0, 0, 0,
0, 0, 1, 0, 0,
0, 0, 0, 1, 0,
]}
/>
</Paint>
}
>
<Skottie animation={animation} frame={frame} />
</Group>
Direct Rendering (Advanced)
For advanced use cases, render directly to a surface:
import { Skia } from "@shopify/react-native-skia";
const surface = Skia.Surface.MakeOffscreen(800, 600);
if (!surface) {
throw new Error("Failed to create surface");
}
const canvas = surface.getCanvas();
// Seek to specific frame
animation.seekFrame(41);
// Render
animation.render(canvas);
surface.flush();
const image = surface.makeImageSnapshot();
- Cache animations outside render when possible
- Reuse animation instances for multiple components
- Use appropriate scaling to avoid large draw areas
- Limit the number of simultaneous animations
- Consider using static frames for non-animated states
- Optimize Lottie files before exporting from After Effects
Troubleshooting
Animation Not Loading
- Verify JSON is valid Lottie format
- Check that
Skia.Skottie.Make() didn’t return null
- Ensure JSON is stringified before passing to
Make()
Missing Assets
- Provide all external assets in the assets object
- Check asset names match exactly
- Verify font and image data is properly loaded
- Reduce animation complexity in After Effects
- Scale down animation dimensions
- Limit frame rate
- Use fewer simultaneous animations
- Consider using video for complex animations
Property Changes Not Visible
- Set properties before rendering first frame
- Verify property keys match animation structure
- Check property value types and ranges
Common Patterns
Loading State
const LoadableSkottie = () => {
const [animation, setAnimation] = useState<SkSkottieAnimation | null>(null);
useEffect(() => {
const json = require("./assets/animation.json");
const anim = Skia.Skottie.Make(JSON.stringify(json));
setAnimation(anim);
}, []);
if (!animation) {
return <Text>Loading...</Text>;
}
return (
<Canvas style={{ flex: 1 }}>
<Skottie animation={animation} frame={0} />
</Canvas>
);
};
Controlled Playback
const ControlledSkottie = () => {
const [isPlaying, setIsPlaying] = useState(true);
const clock = useClock({ paused: !isPlaying });
const frame = useDerivedValue(() => {
if (!isPlaying) return 0;
const fps = animation.fps();
const duration = animation.duration();
return Math.floor((clock.value / 1000) * fps) % (duration * fps);
});
return (
<>
<Canvas style={{ flex: 1 }}>
<Skottie animation={animation} frame={frame} />
</Canvas>
<Button
title={isPlaying ? "Pause" : "Play"}
onPress={() => setIsPlaying(!isPlaying)}
/>
</>
);
};
See Also