React Native Skia brings Google's powerful Skia 2D graphics library to React Native, enabling developers to create stunning custom graphics, animations, and interactive experiences with native performance. This comprehensive guide covers everything you need to know about leveraging Skia in your React Native applications.
What is React Native Skia?
React Native Skia is a library developed by Shopify that brings Google's Skia Graphics Library to React Native. Skia is the same 2D graphics engine that powers Google Chrome, Android UI, Flutter, and many other applications. With React Native Skia, you can create high-performance custom graphics, animations, and interactive components that run at 60+ FPS on both iOS and Android.
Key Features
- High Performance: Native rendering with 60+ FPS animations
- Cross-Platform: Write once, run on iOS, Android, and Web
- Declarative API: React-like component structure for graphics
- Rich Drawing Tools: Paths, shapes, gradients, filters, and more
- Image Processing: Advanced image manipulation and effects
- Animation Support: Smooth animations with React Native Reanimated
When to Use React Native Skia
Perfect Use Cases
✅ Ideal Scenarios
- 📊 Custom charts and data visualizations
- 🎮 2D games and interactive animations
- 🎨 Drawing and sketching applications
- 📐 Custom UI components with complex shapes
- 🖼️ Image filters and photo editing
- 📈 Real-time graphics and dashboards
- 🎯 Custom progress indicators and loaders
⚠️ Consider Alternatives
- 📱 Simple static UI components
- 📝 Text-heavy applications
- 🎬 Video playback (use Video components)
- 🗺️ Maps (use dedicated map libraries)
- 📋 Form inputs and standard controls
- 🔗 Basic navigation and layouts
Getting Started with React Native Skia
Installation
Install React Native Skia using npm or yarn:
# npm
npm install @shopify/react-native-skia
# yarn
yarn add @shopify/react-native-skia
# pnpm
pnpm add @shopify/react-native-skia
For iOS, you need to run pod install:
cd ios && pod install
Basic Setup
Import and use the Canvas component to start drawing:
import React from 'react';
import { Canvas, Circle, Fill } from '@shopify/react-native-skia';
function BasicSkiaExample() {
return (
<Canvas style={{ flex: 1 }}>
<Fill color="lightblue" />
<Circle cx={100} cy={100} r={50} color="red" />
</Canvas>
);
}
Core Skia Components
Drawing Shapes
Skia provides various shape components for different drawing needs:
import {
Canvas,
Circle,
Rect,
RoundedRect,
Path,
Line,
Polygon,
} from '@shopify/react-native-skia';
function ShapesExample() {
return (
<Canvas style={{ width: 300, height: 400 }}>
{/* Circle */}
<Circle cx={50} cy={50} r={30} color="red" />
{/* Rectangle */}
<Rect x={100} y={20} width={60} height={60} color="blue" />
{/* Rounded Rectangle */}
<RoundedRect
x={200}
y={20}
width={60}
height={60}
rx={10}
ry={10}
color="green"
/>
{/* Line */}
<Line p1={{ x: 0, y: 100 }} p2={{ x: 300, y: 100 }} color="orange" strokeWidth={3} />
{/* Polygon */}
<Polygon
points={[
{ x: 150, y: 120 },
{ x: 170, y: 160 },
{ x: 130, y: 160 },
]}
color="purple"
/>
</Canvas>
);
}
Working with Paths
Paths are powerful for creating complex shapes and custom drawings:
import { Canvas, Path, Skia } from '@shopify/react-native-skia';
function PathExample() {
// Create a star path
const star = Skia.Path.Make();
star.moveTo(150, 50);
star.lineTo(160, 80);
star.lineTo(190, 80);
star.lineTo(170, 100);
star.lineTo(180, 130);
star.lineTo(150, 110);
star.lineTo(120, 130);
star.lineTo(130, 100);
star.lineTo(110, 80);
star.lineTo(140, 80);
star.close();
return (
<Canvas style={{ width: 300, height: 200 }}>
<Path path={star} color="gold" style="fill" />
<Path path={star} color="orange" style="stroke" strokeWidth={2} />
</Canvas>
);
}
Advanced Features
Gradients and Effects
Create stunning visual effects with gradients and filters:
import {
Canvas,
Circle,
LinearGradient,
RadialGradient,
Blur,
DropShadow,
vec,
} from '@shopify/react-native-skia';
function GradientExample() {
return (
<Canvas style={{ width: 300, height: 200 }}>
{/* Linear Gradient */}
<Circle cx={75} cy={75} r={50}>
<LinearGradient
start={vec(0, 0)}
end={vec(150, 150)}
colors={['#FF6B6B', '#4ECDC4']}
/>
</Circle>
{/* Radial Gradient with Blur */}
<Circle cx={225} cy={75} r={50}>
<RadialGradient
c={vec(225, 75)}
r={50}
colors={['#A8E6CF', '#7FCDCD']}
/>
<Blur blur={2} />
</Circle>
{/* Drop Shadow Effect */}
<Circle cx={150} cy={150} r={40} color="white">
<DropShadow dx={5} dy={5} blur={10} color="rgba(0,0,0,0.3)" />
</Circle>
</Canvas>
);
}
Animations with Reanimated
Combine Skia with React Native Reanimated for smooth animations:
import React from 'react';
import { Canvas, Circle, Group } from '@shopify/react-native-skia';
import { useSharedValue, withRepeat, withTiming, useDerivedValue } from 'react-native-reanimated';
function AnimatedCircle() {
const progress = useSharedValue(0);
React.useEffect(() => {
progress.value = withRepeat(
withTiming(1, { duration: 2000 }),
-1,
true
);
}, []);
const animatedProps = useDerivedValue(() => {
const scale = 0.5 + progress.value * 0.5;
const rotation = progress.value * 2 * Math.PI;
return {
transform: [
{ scale },
{ rotate: rotation },
],
};
});
return (
<Canvas style={{ width: 200, height: 200 }}>
<Group {...animatedProps}>
<Circle cx={100} cy={100} r={40} color="blue" />
</Group>
</Canvas>
);
}
Real-World Examples
Custom Progress Circle
Build a customizable progress indicator:
import React from 'react';
import { Canvas, Circle, Path, Skia, Text, useFont } from '@shopify/react-native-skia';
type ProgressCircleProps = {
progress: number; // 0 to 1
size: number;
strokeWidth: number;
color: string;
}
function ProgressCircle(props: ProgressCircleProps) {
const { progress, size, strokeWidth, color } = props;
const radius = (size - strokeWidth) / 2;
const circumference = 2 * Math.PI * radius;
const strokeDasharray = circumference;
const strokeDashoffset = circumference * (1 - progress);
// Create circular path
const path = Skia.Path.Make();
path.addCircle(size / 2, size / 2, radius);
return (
<Canvas style={{ width: size, height: size }}>
{/* Background circle */}
<Circle
cx={size / 2}
cy={size / 2}
r={radius}
style="stroke"
strokeWidth={strokeWidth}
color="rgba(0,0,0,0.1)"
/>
{/* Progress circle */}
<Path
path={path}
style="stroke"
strokeWidth={strokeWidth}
color={color}
strokeCap="round"
strokeDasharray={[strokeDasharray]}
strokeDashoffset={strokeDashoffset}
/>
{/* Progress text */}
<Text
x={size / 2}
y={size / 2}
text={`${Math.round(progress * 100)}%`}
color="black"
size={16}
textAlign="center"
/>
</Canvas>
);
}
Interactive Drawing Canvas
Create a drawing app with gesture handling:
import React from 'react';
import { Canvas, Path, Skia } from '@shopify/react-native-skia';
import { Gesture, GestureDetector } from 'react-native-gesture-handler';
import { useSharedValue } from 'react-native-reanimated';
function DrawingCanvas() {
const paths = useSharedValue([]);
const currentPath = useSharedValue(null);
const panGesture = Gesture.Pan()
.onStart((event) => {
const newPath = Skia.Path.Make();
newPath.moveTo(event.x, event.y);
currentPath.value = newPath;
})
.onUpdate((event) => {
if (currentPath.value) {
currentPath.value.lineTo(event.x, event.y);
}
})
.onEnd(() => {
if (currentPath.value) {
paths.value = [...paths.value, currentPath.value];
currentPath.value = null;
}
});
return (
<GestureDetector gesture={panGesture}>
<Canvas style={{ flex: 1 }}>
{paths.value.map((path, index) => (
<Path
key={index}
path={path}
style="stroke"
strokeWidth={3}
color="blue"
strokeCap="round"
strokeJoin="round"
/>
))}
{currentPath.value && (
<Path
path={currentPath.value}
style="stroke"
strokeWidth={3}
color="blue"
strokeCap="round"
strokeJoin="round"
/>
)}
</Canvas>
</GestureDetector>
);
}
Performance Optimization
Best Practices
- Use useMemo for Complex Paths: Cache expensive path calculations
- Minimize Redraws: Only update when necessary using useSharedValue
- Optimize Image Processing: Use appropriate image formats and sizes
- Batch Operations: Group multiple drawing operations when possible
- Profile Performance: Use React Native Performance Monitor
import React, { useMemo } from 'react';
import { Canvas, Path, Skia } from '@shopify/react-native-skia';
function OptimizedComponent({ data }) {
// Memoize expensive path creation
const chartPath = useMemo(() => {
const path = Skia.Path.Make();
data.forEach((point, index) => {
if (index === 0) {
path.moveTo(point.x, point.y);
} else {
path.lineTo(point.x, point.y);
}
});
return path;
}, [data]);
return (
<Canvas style={{ width: 300, height: 200 }}>
<Path path={chartPath} style="stroke" color="blue" strokeWidth={2} />
</Canvas>
);
}
Common Patterns and Tips
Working with Images
Load and manipulate images efficiently:
import { Canvas, Image, useImage } from '@shopify/react-native-skia';
function ImageExample() {
const image = useImage(require('./assets/sample.png'));
if (!image) {
return null; // Loading state
}
return (
<Canvas style={{ width: 300, height: 200 }}>
<Image
image={image}
x={0}
y={0}
width={300}
height={200}
fit="cover"
/>
</Canvas>
);
}
Text Rendering
Render custom text with fonts:
import { Canvas, Text, useFont } from '@shopify/react-native-skia';
function TextExample() {
const font = useFont(require('./assets/fonts/custom-font.ttf'), 24);
if (!font) {
return null;
}
return (
<Canvas style={{ width: 300, height: 100 }}>
<Text
x={20}
y={50}
text="Custom Text Rendering"
font={font}
color="darkblue"
/>
</Canvas>
);
}
Troubleshooting Common Issues
Performance Issues
- Excessive Redraws: Check for unnecessary state updates in parent components
- Memory Leaks: Properly dispose of large images and complex paths
- Animation Stuttering: Ensure animations run on the UI thread with useSharedValue
Platform-Specific Considerations
- iOS: Some gradient effects may render differently; test on device
- Android: Ensure proper hardware acceleration is enabled
- Web: Limited WebGL support may affect complex operations
Conclusion
React Native Skia is a powerful tool for creating high-performance graphics and animations in React Native applications. It's particularly valuable for data visualization, custom UI components, and interactive experiences that require smooth animations and complex drawing operations.
When deciding whether to use Skia, consider the complexity of your graphics needs, performance requirements, and development timeline. For simple UI elements, standard React Native components may be sufficient. However, for custom charts, drawing applications, or complex animations, Skia provides unmatched performance and flexibility.
Start with simple examples and gradually build complexity as you become more comfortable with the API. The combination of Skia's power and React Native's cross-platform capabilities opens up exciting possibilities for creating truly native-feeling applications with stunning visual experiences.