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.
Textures allow you to render React components (including React Native Skia components) as images that can be used in animations and effects. This enables powerful composition patterns and performance optimizations.
useTexture
Render a React element as a texture:
import { useTexture, Canvas, Image, Circle, Fill } from "@shopify/react-native-skia";
import { useSharedValue, useDerivedValue } from "react-native-reanimated";
const TextureDemo = () => {
const rotation = useSharedValue(0);
// Render a React element as a texture
const texture = useTexture(
<Circle cx={64} cy={64} r={40} color="blue">
<Fill color="red" />
</Circle>,
{ width: 128, height: 128 }
);
const transform = useDerivedValue(() => [
{ rotate: rotation.value }
]);
if (!texture.value) return null;
return (
<Canvas style={{ width: 256, height: 256 }}>
<Image
image={texture}
x={64}
y={64}
width={128}
height={128}
transform={transform}
/>
</Canvas>
);
};
Parameters
| Name | Type | Description |
|---|
| element | ReactElement | React element to render |
| size | SkSize | Texture size { width, height } |
| deps | DependencyList | Dependencies for re-rendering |
Use Cases
Complex Graphics as Textures
Render complex graphics once and reuse as textures:
import { useTexture, Canvas, Image } from "@shopify/react-native-skia";
const ComplexGraphicTexture = () => {
const complexGraphic = (
<Group>
<Circle cx={64} cy={64} r={40} color="#FF6B6B" />
<Circle cx={80} cy={50} r={30} color="#4ECDC4" opacity={0.7} />
<Circle cx={50} cy={80} r={30} color="#45B7D1" opacity={0.7} />
<Text text="Hello" x={32} y={70} font={font} color="white" />
</Group>
);
const texture = useTexture(complexGraphic, { width: 128, height: 128 });
if (!texture.value) return null;
return (
<Canvas style={{ flex: 1 }}>
{/* Render the texture multiple times efficiently */}
<Image image={texture} x={0} y={0} width={128} height={128} />
<Image image={texture} x={128} y={0} width={128} height={128} />
<Image image={texture} x={0} y={128} width={128} height={128} />
<Image image={texture} x={128} y={128} width={128} height={128} />
</Canvas>
);
};
Animated Textures
Animate textures with transforms:
import { useTexture, Canvas, Image } from "@shopify/react-native-skia";
import { useSharedValue, withRepeat, withTiming, useDerivedValue } from "react-native-reanimated";
import { useEffect } from "react";
const AnimatedTextureDemo = () => {
const rotation = useSharedValue(0);
const scale = useSharedValue(1);
useEffect(() => {
rotation.value = withRepeat(
withTiming(Math.PI * 2, { duration: 3000 }),
-1,
false
);
scale.value = withRepeat(
withSequence(
withTiming(1.2, { duration: 1000 }),
withTiming(0.8, { duration: 1000 })
),
-1,
true
);
}, []);
const graphic = (
<RoundedRect x={0} y={0} width={100} height={100} r={20} color="purple" />
);
const texture = useTexture(graphic, { width: 100, height: 100 });
const transform = useDerivedValue(() => [
{ rotate: rotation.value },
{ scale: scale.value }
]);
if (!texture.value) return null;
return (
<Canvas style={{ width: 256, height: 256 }}>
<Image
image={texture}
x={78}
y={78}
width={100}
height={100}
transform={transform}
origin={{ x: 128, y: 128 }}
/>
</Canvas>
);
};
Texture with Dependencies
Re-render texture when dependencies change:
import { useTexture } from "@shopify/react-native-skia";
import { useState } from "react";
const DynamicTextureDemo = () => {
const [color, setColor] = useState("red");
const [text, setText] = useState("Hello");
// Texture re-renders when color or text changes
const texture = useTexture(
<Group>
<Circle cx={64} cy={64} r={50} color={color} />
<Text text={text} x={32} y={70} font={font} color="white" />
</Group>,
{ width: 128, height: 128 },
[color, text] // Dependencies
);
if (!texture.value) return null;
return (
<View>
<Canvas style={{ width: 128, height: 128 }}>
<Image image={texture} x={0} y={0} width={128} height={128} />
</Canvas>
<Button title="Red" onPress={() => setColor("red")} />
<Button title="Blue" onPress={() => setColor("blue")} />
</View>
);
};
usePictureAsTexture
Convert a SkPicture to a texture:
import { usePictureAsTexture, Skia } from "@shopify/react-native-skia";
import { useEffect, useState } from "react";
const PictureTextureDemo = () => {
const [picture, setPicture] = useState<SkPicture | null>(null);
useEffect(() => {
const pic = Skia.Picture.MakePicture((canvas) => {
const paint = Skia.Paint();
paint.setColor(Skia.Color("blue"));
canvas.drawCircle(64, 64, 50, paint);
}, { x: 0, y: 0, width: 128, height: 128 });
setPicture(pic);
}, []);
const texture = usePictureAsTexture(picture, { width: 128, height: 128 });
if (!texture.value) return null;
return (
<Canvas style={{ width: 256, height: 256 }}>
<Image image={texture} x={64} y={64} width={128} height={128} />
</Canvas>
);
};
useImageAsTexture
Use an image as a texture with automatic conversion:
import { useImageAsTexture, Canvas, Image } from "@shopify/react-native-skia";
const ImageTextureDemo = () => {
const texture = useImageAsTexture(require("./photo.jpg"));
if (!texture.value) return null;
return (
<Canvas style={{ flex: 1 }}>
<Image
image={texture}
x={0}
y={0}
width={256}
height={256}
fit="cover"
/>
</Canvas>
);
};
Rendering Complex Graphics Once
Textures are rendered once and cached:
// Bad: Re-renders complex graphic every frame
const BadExample = () => {
const rotation = useSharedValue(0);
return (
<Canvas style={{ flex: 1 }}>
<Group transform={[{ rotate: rotation }]}>
{/* Complex graphic re-rendered every frame */}
<Circle cx={64} cy={64} r={40} color="red" />
<Circle cx={80} cy={50} r={30} color="blue" />
<Circle cx={50} cy={80} r={30} color="green" />
{/* ... many more shapes ... */}
</Group>
</Canvas>
);
};
// Good: Renders once, animates as texture
const GoodExample = () => {
const rotation = useSharedValue(0);
const graphic = (
<Group>
<Circle cx={64} cy={64} r={40} color="red" />
<Circle cx={80} cy={50} r={30} color="blue" />
<Circle cx={50} cy={80} r={30} color="green" />
{/* ... many more shapes ... */}
</Group>
);
const texture = useTexture(graphic, { width: 128, height: 128 });
if (!texture.value) return null;
return (
<Canvas style={{ flex: 1 }}>
<Image
image={texture}
x={0}
y={0}
width={128}
height={128}
transform={[{ rotate: rotation }]}
/>
</Canvas>
);
};
Reusing Graphics
Textures can be reused multiple times efficiently:
const ParticleSystem = () => {
const particleTexture = useTexture(
<Circle cx={8} cy={8} r={8} color="white">
<BlurMask blur={4} style="solid" />
</Circle>,
{ width: 16, height: 16 }
);
if (!particleTexture.value) return null;
return (
<Canvas style={{ flex: 1 }}>
{/* Render 100 particles using the same texture */}
{Array.from({ length: 100 }).map((_, i) => (
<Image
key={i}
image={particleTexture}
x={Math.random() * 256}
y={Math.random() * 256}
width={16}
height={16}
opacity={Math.random()}
/>
))}
</Canvas>
);
};
Texture Filters
Apply filters to textures:
import { useTexture, Canvas, Image, Blur } from "@shopify/react-native-skia";
const FilteredTextureDemo = () => {
const graphic = (
<Circle cx={64} cy={64} r={50} color="cyan" />
);
const texture = useTexture(graphic, { width: 128, height: 128 });
if (!texture.value) return null;
return (
<Canvas style={{ width: 256, height: 256 }}>
<Image image={texture} x={0} y={0} width={128} height={128}>
<Blur blur={10} />
</Image>
</Canvas>
);
};
On web, textures are automatically converted to non-texture images for compatibility:
// Handled automatically by React Native Skia
if (Platform.OS === "web") {
texture.value = texture.value.makeNonTextureImage();
}
Memory Management
Textures consume GPU memory. Clean up when no longer needed:
import { useEffect } from "react";
const TextureCleanupDemo = () => {
const texture = useTexture(myElement, { width: 256, height: 256 });
useEffect(() => {
return () => {
// Texture is automatically cleaned up
};
}, []);
// ...
};
Common Patterns
Texture Atlas
Combine multiple graphics into a single texture:
const AtlasDemo = () => {
const atlas = useTexture(
<Group>
<Circle cx={32} cy={32} r={20} color="red" />
<Circle cx={96} cy={32} r={20} color="green" />
<Circle cx={32} cy={96} r={20} color="blue" />
<Circle cx={96} cy={96} r={20} color="yellow" />
</Group>,
{ width: 128, height: 128 }
);
if (!atlas.value) return null;
return (
<Canvas style={{ flex: 1 }}>
{/* Use different parts of the atlas */}
<Image
image={atlas}
x={0}
y={0}
width={64}
height={64}
rect={{ x: 0, y: 0, width: 64, height: 64 }}
/>
</Canvas>
);
};
Cached Rendering
Cache expensive rendering operations:
const CachedChartDemo = () => {
const [data, setData] = useState([...]);
const chartTexture = useTexture(
<LineChart data={data} />,
{ width: 300, height: 200 },
[data] // Only re-render when data changes
);
if (!chartTexture.value) return null;
return (
<Canvas style={{ width: 300, height: 200 }}>
<Image image={chartTexture} x={0} y={0} width={300} height={200} />
</Canvas>
);
};
Limitations
- Textures are rendered at a fixed size
- Re-rendering requires re-creating the texture
- GPU memory usage increases with texture count
- Not suitable for frequently changing content
Best Practices
- Use textures for complex, static graphics
- Reuse textures when rendering the same graphic multiple times
- Specify dependencies to control when textures update
- Keep texture sizes reasonable to manage memory
- Prefer direct rendering for simple shapes
- Clean up textures when components unmount