Skip to main content

React Native Skia vs React Native SVG vs react-native-canvas 2026

·PkgPulse Team

React Native Skia vs React Native SVG vs react-native-canvas 2026

TL;DR

Drawing 2D graphics in React Native — charts, custom UI, image filters, signatures, games, data visualization — requires choosing the right rendering layer. React Native Skia (from Shopify) runs Google's Skia graphics library on the UI thread via JSI (zero JS bridge), supports Reanimated animations, image filters, shaders, and text on paths — it's the most powerful option and ships in Expo SDK 46+. React Native SVG renders SVG primitives (<Rect>, <Circle>, <Path>, <Text>) using native SVG engines (AVFoundation on iOS, SVG on Android), integrates with React's component model naturally, and handles icon libraries and custom vector graphics well. react-native-canvas wraps a WebView with an HTML5 Canvas API — useful for porting web Canvas code to mobile but limited by WebView performance and the JS bridge overhead. For high-performance animations, image processing, and complex 2D rendering: React Native Skia. For vector graphics, icons, charts, and SVG-based custom UI: React Native SVG. For porting existing HTML5 Canvas code with minimal changes: react-native-canvas.

Key Takeaways

  • Skia runs on UI thread — JSI + Worklets, zero bridge overhead, same thread as Reanimated
  • Skia supports image filters — blur, color matrix, displacement maps, shader effects
  • React Native SVG is the SVG standard — declarative primitives, no setup beyond install
  • Skia supports shaders — GLSL-like sksl shaders for complex graphical effects
  • React Native SVG integrates with Victory Native — charting library on top of SVG
  • react-native-canvas bridges WebView — compatible with web Canvas API but slowest
  • Skia requires New Architecture or JSI — works with both Old and New arch via JSI bridge

Performance Comparison

React Native Skia      JSI direct, UI thread, Reanimated-native
React Native SVG       Native SVG engine, bridge serialization for updates
react-native-canvas    WebView + postMessage, slowest, most compatible

React Native Skia: JSI-Powered 2D Graphics

React Native Skia wraps Google's Skia graphics engine (same as Flutter, Chrome, Android) with a React-based declarative API and JSI for zero-bridge rendering.

Installation

# For Expo:
npx expo install @shopify/react-native-skia

# For bare React Native:
npm install @shopify/react-native-skia
cd ios && pod install

Basic Shapes

import { Canvas, Circle, Rect, RoundedRect, Path, vec } from "@shopify/react-native-skia";

export function BasicShapes() {
  return (
    <Canvas style={{ width: 300, height: 300 }}>
      {/* Circle */}
      <Circle cx={80} cy={80} r={50} color="#6366f1" />

      {/* Rectangle */}
      <Rect x={150} y={30} width={100} height={100} color="#ec4899" />

      {/* Rounded Rectangle */}
      <RoundedRect
        x={50}
        y={180}
        width={200}
        height={80}
        r={12}
        color="#06b6d4"
      />

      {/* Custom path */}
      <Path
        path="M 40 240 L 100 180 L 160 240 Z"
        color="#10b981"
        style="fill"
      />
    </Canvas>
  );
}

Gradients and Paint

import {
  Canvas,
  Rect,
  Circle,
  LinearGradient,
  RadialGradient,
  SweepGradient,
  Paint,
  vec,
} from "@shopify/react-native-skia";

export function GradientShapes() {
  return (
    <Canvas style={{ width: 300, height: 300 }}>
      {/* Rect with linear gradient */}
      <Rect x={20} y={20} width={120} height={120}>
        <LinearGradient
          start={vec(20, 20)}
          end={vec(140, 140)}
          colors={["#6366f1", "#ec4899"]}
        />
      </Rect>

      {/* Circle with radial gradient */}
      <Circle cx={210} cy={80} r={60}>
        <RadialGradient
          c={vec(210, 80)}
          r={60}
          colors={["#fbbf24", "#f97316", "#ef4444"]}
        />
      </Circle>

      {/* Stroke with paint */}
      <Circle cx={80} cy={220} r={50}>
        <Paint
          style="stroke"
          strokeWidth={4}
          color="#8b5cf6"
        />
      </Circle>
    </Canvas>
  );
}

Animated with Reanimated

import { Canvas, Circle, Fill } from "@shopify/react-native-skia";
import { useEffect } from "react";
import {
  useDerivedValue,
  useSharedValue,
  withRepeat,
  withTiming,
} from "react-native-reanimated";

export function PulsatingCircle() {
  const progress = useSharedValue(0);

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

  // Derived values run on UI thread — no bridge
  const radius = useDerivedValue(() => 40 + progress.value * 30);
  const opacity = useDerivedValue(() => 1 - progress.value * 0.5);

  return (
    <Canvas style={{ width: 200, height: 200 }}>
      <Circle
        cx={100}
        cy={100}
        r={radius} // Animatedupdates on UI thread
        color="#6366f1"
        opacity={opacity}
      />
    </Canvas>
  );
}

Image Filters (Blur, Color Matrix)

import {
  Canvas,
  Image,
  Blur,
  ColorMatrix,
  useImage,
} from "@shopify/react-native-skia";

export function ImageFilters() {
  const image = useImage(require("./photo.jpg"));

  // Grayscale matrix
  const grayscaleMatrix = [
    0.21, 0.72, 0.07, 0, 0,
    0.21, 0.72, 0.07, 0, 0,
    0.21, 0.72, 0.07, 0, 0,
    0, 0, 0, 1, 0,
  ];

  if (!image) return null;

  return (
    <Canvas style={{ width: 300, height: 300 }}>
      {/* Blurred image */}
      <Image x={0} y={0} width={140} height={140} image={image} fit="cover">
        <Blur blur={5} />
      </Image>

      {/* Grayscale image */}
      <Image x={160} y={0} width={140} height={140} image={image} fit="cover">
        <ColorMatrix matrix={grayscaleMatrix} />
      </Image>
    </Canvas>
  );
}

Text and Custom Fonts

import {
  Canvas,
  Text,
  useFont,
  Skia,
  Path,
  TextPath,
} from "@shopify/react-native-skia";

export function SkiaText() {
  const font = useFont(require("./Inter-Regular.ttf"), 24);

  // Text on a circular path
  const path = Skia.Path.Make();
  path.addArc({ x: 50, y: 50, width: 200, height: 200 }, -90, 180);

  if (!font) return null;

  return (
    <Canvas style={{ width: 300, height: 300 }}>
      {/* Basic text */}
      <Text
        x={20}
        y={50}
        text="Hello Skia!"
        font={font}
        color="#e5e7eb"
      />

      {/* Text on a path */}
      <TextPath text="Text on a curve..." path={path} font={font} color="#6366f1" />
    </Canvas>
  );
}

React Native SVG: Declarative Vector Graphics

React Native SVG brings the SVG spec to React Native — same primitives as web SVG, familiar if you know SVG.

Installation

# Expo:
npx expo install react-native-svg

# Bare:
npm install react-native-svg
cd ios && pod install

Basic SVG Shapes

import Svg, {
  Circle,
  Rect,
  Path,
  Ellipse,
  Line,
  Polygon,
  Text as SvgText,
  Defs,
  LinearGradient,
  Stop,
  ClipPath,
  G,
} from "react-native-svg";

export function SvgShapes() {
  return (
    <Svg width={300} height={300}>
      {/* Gradient definition */}
      <Defs>
        <LinearGradient id="grad1" x1="0" y1="0" x2="1" y2="1">
          <Stop offset="0" stopColor="#6366f1" stopOpacity="1" />
          <Stop offset="1" stopColor="#ec4899" stopOpacity="1" />
        </LinearGradient>
      </Defs>

      {/* Circle with gradient */}
      <Circle cx={80} cy={80} r={50} fill="url(#grad1)" />

      {/* Rectangle */}
      <Rect
        x={150}
        y={30}
        width={100}
        height={100}
        rx={12}
        fill="#ec4899"
      />

      {/* Path (arrow) */}
      <Path
        d="M 50 200 L 100 150 L 150 200 L 130 200 L 130 250 L 70 250 L 70 200 Z"
        fill="#10b981"
      />

      {/* Text */}
      <SvgText x={200} y={220} fontSize={16} fill="#e5e7eb" textAnchor="middle">
        SVG Text
      </SvgText>
    </Svg>
  );
}

Custom Icon Component

import Svg, { Path, G } from "react-native-svg";

interface IconProps {
  size?: number;
  color?: string;
}

// Custom SVG icon as a React component
export function CheckCircleIcon({ size = 24, color = "#10b981" }: IconProps) {
  return (
    <Svg width={size} height={size} viewBox="0 0 24 24" fill="none">
      <Path
        d="M22 11.08V12a10 10 0 11-5.93-9.14"
        stroke={color}
        strokeWidth={2}
        strokeLinecap="round"
        strokeLinejoin="round"
      />
      <Path
        d="M22 4L12 14.01l-3-3"
        stroke={color}
        strokeWidth={2}
        strokeLinecap="round"
        strokeLinejoin="round"
      />
    </Svg>
  );
}

Animated SVG with Reanimated

import Svg, { Circle, Path } from "react-native-svg";
import Animated, { useAnimatedProps, useSharedValue, withRepeat, withTiming } from "react-native-reanimated";
import { useEffect } from "react";

const AnimatedCircle = Animated.createAnimatedComponent(Circle);

export function AnimatedProgressRing({
  progress = 0.7,
  size = 100,
  color = "#6366f1",
}: {
  progress?: number;
  size?: number;
  color?: string;
}) {
  const radius = size / 2 - 8;
  const circumference = 2 * Math.PI * radius;
  const animatedProgress = useSharedValue(0);

  useEffect(() => {
    animatedProgress.value = withTiming(progress, { duration: 1000 });
  }, [progress]);

  const animatedProps = useAnimatedProps(() => ({
    strokeDashoffset: circumference * (1 - animatedProgress.value),
  }));

  return (
    <Svg width={size} height={size} viewBox={`0 0 ${size} ${size}`}>
      {/* Background track */}
      <Circle
        cx={size / 2}
        cy={size / 2}
        r={radius}
        stroke="#333"
        strokeWidth={8}
        fill="transparent"
      />
      {/* Progress ring */}
      <AnimatedCircle
        cx={size / 2}
        cy={size / 2}
        r={radius}
        stroke={color}
        strokeWidth={8}
        fill="transparent"
        strokeDasharray={circumference}
        strokeDashoffset={circumference} // Starting value
        animatedProps={animatedProps}
        strokeLinecap="round"
        transform={`rotate(-90, ${size / 2}, ${size / 2})`}
      />
    </Svg>
  );
}

Victory Native (Charting on SVG)

import { VictoryBar, VictoryChart, VictoryTheme, VictoryAxis } from "victory-native";

const data = [
  { x: "Jan", y: 42 },
  { x: "Feb", y: 55 },
  { x: "Mar", y: 67 },
  { x: "Apr", y: 49 },
  { x: "May", y: 72 },
];

export function BarChart() {
  return (
    <VictoryChart theme={VictoryTheme.material} domainPadding={20}>
      <VictoryAxis />
      <VictoryAxis dependentAxis />
      <VictoryBar data={data} style={{ data: { fill: "#6366f1" } }} />
    </VictoryChart>
  );
}

react-native-canvas: Web Canvas in React Native

react-native-canvas wraps a WebView with an HTML5 Canvas API — useful for porting web code.

Installation

npm install react-native-canvas react-native-webview
cd ios && pod install

Basic Canvas Drawing

import React, { useRef } from "react";
import Canvas, { CanvasRenderingContext2D } from "react-native-canvas";
import { View } from "react-native";

export function CanvasExample() {
  function handleCanvas(canvas: Canvas | null) {
    if (!canvas) return;

    canvas.width = 300;
    canvas.height = 300;

    const ctx: CanvasRenderingContext2D = canvas.getContext("2d");

    // Same API as web HTML5 Canvas
    ctx.fillStyle = "#6366f1";
    ctx.fillRect(50, 50, 100, 100);

    ctx.beginPath();
    ctx.arc(200, 100, 50, 0, Math.PI * 2);
    ctx.fillStyle = "#ec4899";
    ctx.fill();

    // Gradient
    const gradient = ctx.createLinearGradient(0, 200, 300, 300);
    gradient.addColorStop(0, "#10b981");
    gradient.addColorStop(1, "#06b6d4");
    ctx.fillStyle = gradient;
    ctx.fillRect(0, 200, 300, 100);
  }

  return (
    <View>
      <Canvas ref={handleCanvas} />
    </View>
  );
}

Feature Comparison

FeatureReact Native SkiaReact Native SVGreact-native-canvas
Performance✅ Best (JSI)Good (native SVG)⚠️ WebView-based
Reanimated✅ Native integration✅ Via AnimatedNo
Shader support✅ SkSL shadersNoLimited
Image filters✅ Blur, ColorMatrixBasicLimited
SVG path API✅ Skia paths✅ SVG specCanvas paths
Web Canvas APIDifferent APINo✅ Compatible
Expo support✅ SDK 46+No
New Architecture✅ JSINo
Text on pathVia SVGCanvas API
Font support✅ Custom fonts✅ System + SVGLimited
Gradients✅ Linear/Radial/Sweep✅ Linear/Radial
ClipPath
Bundle overhead~4MB~500kBSmall (needs WebView)
npm weekly500k2.5M100k
GitHub stars6k7k1.5k

When to Use Each

Choose React Native Skia if:

  • High-performance animations (60fps+ on UI thread)
  • Image processing — blur, color correction, filters
  • Complex 2D rendering — games, drawing apps, signature pads
  • Need shaders for advanced visual effects
  • Using Reanimated extensively (Skia values are worklet-native)
  • Building a data visualization with many animated elements

Choose React Native SVG if:

  • Icon rendering and SVG icon libraries
  • Simple charts via Victory Native or similar
  • Custom UI components with vector shapes
  • Familiar with SVG spec from web development
  • Don't need animations beyond React's basic Animated API
  • Lightweight package, broad compatibility needed

Choose react-native-canvas if:

  • Porting existing web Canvas code to React Native
  • Need the HTML5 Canvas API specifically (same methods)
  • Simple drawing that doesn't require high performance
  • Prototyping — not recommended for production high-performance use cases
  • Learning project translating web to React Native

Methodology

Data sourced from React Native Skia documentation (shopify.github.io/react-native-skia), React Native SVG documentation (github.com/software-mansion/react-native-svg), react-native-canvas documentation (github.com/iddan/react-native-canvas), npm weekly download statistics as of February 2026, GitHub star counts as of February 2026, and performance benchmarks from Shopify's Engineering blog.


Related: React Native Reanimated vs Moti vs Skia animation libraries for animation approaches in React Native, or Victory Native vs react-native-chart-kit vs echarts RN charts for charting specifically.

Comments

Stay Updated

Get the latest package insights, npm trends, and tooling tips delivered to your inbox.