Skip to main content
React Native Skia supports two rendering paradigms: Retained Mode and Immediate Mode. Understanding when to use each is key to building performant graphics applications. The Retained Mode allows for extremely fast animation with virtually zero FFI cost if the drawing list is updated at low frequency. Immediate mode allows for dynamic drawing lists but has a higher FFI cost. Since both modes use the same <Canvas> element, you can seamlessly combine them in a single scene.

Retained Mode (Default)

In retained mode, you declare your scene as a tree of React components. React Native Skia converts this tree into a display list that is extremely efficient to animate with Reanimated. This approach is extremely fast and best suited for user interfaces and interactive graphics where the structure doesn’t change at animation time.
import React, { useEffect } from "react";
import { Canvas, Circle, Group } from "@shopify/react-native-skia";
import { 
  useSharedValue, 
  withSpring 
} from "react-native-reanimated";

export const RetainedModeExample = () => {
  const radius = useSharedValue(50);
  
  useEffect(() => {
    radius.value = withSpring(radius.value === 50 ? 100 : 50);
  }, []);
  
  return (
    <Canvas style={{ flex: 1 }}>
      <Group>
        <Circle cx={128} cy={128} r={radius} color="cyan" />
      </Group>
    </Canvas>
  );
};

Immediate Mode

In immediate mode, you issue drawing commands directly to a canvas on every frame. This gives you complete control over what gets drawn and when, but requires you to manage the drawing logic yourself. React Native Skia provides immediate mode through the Picture API. This mode is well-suited for scenes where the number of drawing commands changes on every animation frame, such as:
  • Games
  • Generative art
  • Particle systems
  • Dynamic visualizations
import { Canvas, Picture, Skia } from "@shopify/react-native-skia";
import { 
  useDerivedValue, 
  useSharedValue, 
  withRepeat, 
  withTiming 
} from "react-native-reanimated";
import { useEffect } from "react";

const size = 256;

export const ImmediateModeExample = () => {
  const progress = useSharedValue(0);
  const recorder = Skia.PictureRecorder();
  const paint = Skia.Paint();

  useEffect(() => {
    progress.value = withRepeat(
      withTiming(1, { duration: 2000 }), 
      -1, 
      true
    );
  }, [progress]);

  const picture = useDerivedValue(() => {
    "worklet";
    const canvas = recorder.beginRecording(
      Skia.XYWHRect(0, 0, size, size)
    );

    // Variable number of circles based on progress
    const count = Math.floor(progress.value * 20);
    for (let i = 0; i < count; i++) {
      const r = (i + 1) * 6;
      paint.setColor(
        Skia.Color(`rgba(0, 122, 255, ${(i + 1) / 20})`)
      );
      canvas.drawCircle(size / 2, size / 2, r, paint);
    }

    return recorder.finishRecordingAsPicture();
  });

  return (
    <Canvas style={{ flex: 1 }}>
      <Picture picture={picture} />
    </Canvas>
  );
};

Choosing the Right Mode

Here’s a guide to help you choose the appropriate rendering mode:
ScenarioRecommended ModeWhy
UI with animated propertiesRetainedZero FFI cost during animation
Data visualizationRetainedStructure usually fixed
Fixed number of sprites/tilesRetainedWith Atlas API, single draw call
Game with dynamic entitiesImmediateEntities created/destroyed
Procedural/generative artImmediateDynamic drawing commands
Particle systemsImmediateVariable particle count
Static graphicsRetainedSimplest API

Combining Modes

You can combine both rendering modes in a single scene. For example, a game where the scene is dynamic (immediate mode) but the UI elements are built in retained mode.
import { Canvas, Picture, Rect } from "@shopify/react-native-skia";

const HybridExample = () => {
  return (
    <Canvas style={{ flex: 1 }}>
      {/* Immediate mode for dynamic content */}
      <Picture picture={dynamicPicture} />
      
      {/* Retained mode for UI elements */}
      <Rect x={10} y={10} width={100} height={40} color="white" />
    </Canvas>
  );
};

Build docs developers (and LLMs) love