Skip to main content

React Native Skia vs SVG vs Canvas: 2D Graphics 2026

·PkgPulse Team
0

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 Skia and Reanimated Integration

The most powerful aspect of React Native Skia is its integration with Reanimated 2+. Skia values can be driven directly by Reanimated shared values, enabling 60fps animations that run entirely on the UI thread without ever touching the JS thread. This architecture eliminates the common React Native animation bottleneck where heavy JS work causes dropped frames.

import { useSharedValue, withSpring } from 'react-native-reanimated';
import { Canvas, Circle, useValue } from '@shopify/react-native-skia';

function AnimatedCircle() {
  const radius = useSharedValue(50);
  
  // Directly connected to Skia — no bridge crossing
  return (
    <Canvas style={{ flex: 1 }}>
      <Circle cx={100} cy={100} r={radius} color="#6366f1" />
    </Canvas>
  );
}

This direct connection to Reanimated makes React Native Skia the go-to choice for building gesture-driven animations — scroll-linked effects, drag interactions, pinch-to-zoom behaviors — that require consistent 60fps performance even on mid-range Android devices. The equivalent in react-native-svg would require careful JavaScript optimization and still risks dropped frames on heavier interactions.

Bundle Size and Installation Considerations

React Native Skia is a significant dependency. The Skia graphics engine itself adds roughly 10-15 MB to the native binary on both iOS and Android. For apps where binary size is constrained — certain enterprise MDM deployments, low-storage device targets — this overhead matters. React Native SVG adds less than 2 MB to the binary, making it the lightweight option for apps that don't need Skia's advanced capabilities.

The Expo managed workflow supports React Native Skia via expo-skia, but it requires using a custom dev client (Expo Go doesn't include Skia by default). Teams that rely on Expo Go for development testing need to plan for this change. React Native SVG is included in Expo's default configuration and works in Expo Go without any additional setup.


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. It is the least performant of the three options because every drawing command crosses the bridge from React Native to a WebView, introducing latency on every frame. However, for applications that have existing Canvas-based logic from a web project, it can dramatically reduce the porting effort by allowing the same drawing code to run in React Native with minimal changes.

The architecture of react-native-canvas means it inherits WebView's limitations: slow startup (WebView initialization adds 100-300ms before the canvas is ready), limited access to native APIs, and memory overhead from running a full browser rendering engine just for 2D graphics. In 2026, most teams that previously reached for react-native-canvas are instead using React Native Skia for new development — the performance difference is significant enough that the rewrite cost is typically worth it.

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

Ecosystem and Community

React Native SVG is maintained by Software Mansion, the same team behind React Native Reanimated and React Native Gesture Handler. This gives it exceptional reliability and active maintenance — the library is updated alongside every major React Native version. With 2.5M weekly npm downloads, it is the most widely deployed React Native graphics library and is considered part of the standard React Native toolkit. Victory Native, the popular charting library from FormidableLabs, builds directly on react-native-svg, making it the natural choice when charting is your primary use case.

React Native Skia is a Shopify Engineering project with clear long-term commitment — Shopify uses it in their consumer-facing apps. The library ships in Expo SDK as a first-class package, which means the Expo team validates it with every SDK release. The Skia documentation site (shopify.github.io/react-native-skia) is comprehensive with interactive examples. The community Discord is active and Shopify engineers respond directly to issues. As of early 2026, Skia has become the standard for game-like and highly animated interfaces in React Native.

react-native-canvas is a community project without a major corporate backer. The library is functional but maintenance has been intermittent. It sees about 100k weekly downloads, primarily from developers porting web Canvas code or following older tutorials. For new projects, it is rarely the right choice — React Native Skia provides the Canvas-like imperative API with vastly better performance for any use case that truly requires it.


Real-World Adoption

React Native Skia has been adopted by Shopify for their Shop app, which renders complex custom UI and animations. The drawing and annotation features in productivity apps like Notability-style experiences use Skia for signature pads and freehand drawing. Games built with React Native leverage Skia for sprite rendering and particle effects at 60fps. Several fintech apps use Skia for animated chart visualizations that need to respond to real-time data.

React Native SVG dominates in icon systems and static or lightly-animated charts. Libraries like react-native-vector-icons internally use react-native-svg primitives, and virtually every React Native app that displays vector icons is using this library indirectly. The Victory Native charting suite has thousands of production deployments in data-heavy apps like analytics dashboards and fitness trackers.


Developer Experience Deep Dive

React Native Skia has excellent TypeScript types generated directly from the Skia API. Debugging is aided by the Skia debugger support and the ability to render to offscreen canvases for testing. The main developer friction is understanding the Skia drawing model — concepts like Paint objects, Skia paths, and the clip/mask system differ from both SVG and Canvas API. The learning curve is steeper than react-native-svg but the documentation and community examples cover virtually every use case.

React Native SVG has the lowest learning curve of the three. If you know SVG from web development, the React Native API is almost identical — <Rect>, <Circle>, <Path>, <LinearGradient> map directly. TypeScript support is excellent. The main limitation is that complex animations require Animated.createAnimatedComponent() wrappers, which adds boilerplate compared to Skia's native Reanimated integration.


Performance and Benchmarks

React Native Skia achieves consistent 60fps animations because it runs on the UI thread via JSI rather than the JavaScript thread. A Skia canvas with 50 animated circles updating per frame has been benchmarked by the Shopify engineering team at under 2ms of frame budget overhead on an iPhone 13 Pro. Image blur operations that would cause frame drops when computed on the JS thread complete within a single frame when delegated to Skia's GPU-accelerated pipeline. Shopify's internal testing showed that complex data visualizations with 500+ data points animated via Reanimated achieve smoother scrolling and zoom with Skia than with equivalent SVG-based implementations.

React Native SVG performance is excellent for static and lightly-animated content. The native SVG engine on both iOS and Android is GPU-accelerated and handles thousands of path elements efficiently. The bottleneck for React Native SVG is animated updates — each animated prop change serializes across the JavaScript bridge (unless using Reanimated's animated component wrapper), which creates measurable frame drop risk for complex animations with many elements changing simultaneously. Victory Native, which uses React Native SVG, addresses this by minimizing the number of bridge crossings for chart updates.

react-native-canvas performance is bounded by WebView overhead. The HTML5 Canvas API communicates with the WebView via postMessage, meaning every drawing operation waits for an inter-process message round-trip. For static content drawn once (like a complex diagram), this overhead is invisible after the initial render. For animations, the round-trip delay makes smooth frame rates essentially unachievable — each frame requires sending drawing commands to the WebView and waiting for confirmation. This is the fundamental architectural limitation that makes react-native-canvas unsuitable for real-time graphics.


Migration Guide

Moving from react-native-canvas to React Native Skia is the most common migration path as teams outgrow WebView-based rendering. The APIs are conceptually different — Skia uses a declarative component model while Canvas uses an imperative drawing context. The key translation is: Canvas's fillRect(x, y, w, h) becomes <Rect x y width height />, arc(cx, cy, r) becomes <Circle cx cy r />, and drawPath() becomes <Path path="..." />. Image filter calls (ctx.filter = "blur(5px)") become Skia's <Blur blur={5} /> child component.


Final Verdict 2026

React Native Skia is the clear winner for any use case involving animation, image processing, or complex custom rendering. Its JSI architecture and Reanimated integration make 60fps animations straightforward. For a standard app with icons, simple charts, and occasional custom shapes, React Native SVG remains the lighter and more universally compatible choice. react-native-canvas should only be considered when porting existing web Canvas code and a rewrite is not feasible.


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

Cross-Platform Considerations

All three libraries support both iOS and Android, but their platform behavior differs in ways that matter for production apps. React Native Skia uses Google's Skia engine, which is the same graphics library used by Android natively — on Android, Skia rendering is essentially zero overhead since it's using the platform's own graphics stack. On iOS, Skia runs alongside Metal, Apple's native graphics API, which means there's a thin interop layer. In practice, the performance difference between iOS and Android in React Native Skia is negligible.

React Native SVG has historically had more iOS/Android behavioral differences. Gradient rendering, text alignment, and certain filter operations have had platform-specific quirks that teams only discovered in production. The library has improved substantially, but teams building for both platforms should test critical SVG rendering on both iOS and Android explicitly.

react-native-canvas's WebView foundation means it behaves consistently across platforms (web rendering is cross-platform by design), but this consistency comes at the cost of using the WebView engine rather than native graphics — which is slower on both platforms.

For apps targeting React Native Web (web via Metro bundler), React Native Skia provides a web implementation that renders to an HTML5 Canvas element. React Native SVG similarly renders to SVG elements on web. react-native-canvas degrades more gracefully on web since it's already Canvas-based. Teams building truly cross-platform apps (iOS + Android + web) should test their chosen library's web behavior early, as the web targets for these libraries have historically been less mature than the native targets.

Performance Benchmarks in Context

Benchmark numbers for React Native graphics libraries should be interpreted carefully. The headline "React Native Skia renders 60fps" is true for animations driven by Reanimated on the UI thread. However, if your Skia rendering logic runs on the JS thread, you can still drop frames — the JSI architecture enables UI-thread rendering but doesn't force it.

For React Native SVG, the performance ceiling is lower, but for the typical use case (icons, simple charts, static vector artwork), this ceiling is never reached. An app with 50 SVG icons on screen performs identically with react-native-svg and React Native Skia because neither library is the bottleneck — the React reconciler and JavaScript runtime are.

The practical performance guidance: if you're animating graphics in response to gestures or scroll position, use React Native Skia with Reanimated worklets. If you're rendering static or React-state-animated vector graphics, either Skia or SVG performs adequately. If you're experiencing frame drops with react-native-svg in production, profile before assuming the library is the cause — layout and JavaScript bundle size are typically higher-impact improvements.


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:

NativeWind vs Tamagui vs twrnc for styling approaches in React Native apps that use these graphics libraries.

Best React component libraries 2026 for web component ecosystems.

Best JavaScript testing frameworks 2026 for testing strategies that apply to React Native projects.

Choosing the right graphics library early prevents costly rewrites as your feature set grows and animation complexity increases.

The 2026 JavaScript Stack Cheatsheet

One PDF: the best package for every category (ORMs, bundlers, auth, testing, state management). Used by 500+ devs. Free, updated monthly.