DEV Community

Cover image for React Native Skia: The Future of High-Performance UI
Sharad K
Sharad K

Posted on

React Native Skia: The Future of High-Performance UI

React Native Skia: The Future of High-Performance UI

React Native has revolutionized mobile app development, but when it comes to creating complex animations and custom graphics, developers often hit performance walls. Enter React Native Skia – a game-changing library that brings Google's powerful Skia graphics engine directly to your React Native apps, enabling buttery-smooth 60fps animations and stunning visual effects.

What is React Native Skia?

React Native Skia is a 2D graphics library that provides direct access to Skia's rendering capabilities on both iOS and Android. Unlike traditional React Native animations that rely on the JavaScript bridge, Skia renders everything natively on the UI thread, eliminating performance bottlenecks and enabling truly smooth animations.

Why Skia is Taking Over

🚀 Native Performance

Skia runs directly on the native thread, bypassing the JavaScript bridge entirely. This means your animations run at true 60fps without frame drops.

🎨 Unlimited Creative Freedom

Create complex shapes, gradients, filters, and effects that would be impossible or extremely difficult with standard React Native components.

📊 Perfect for Data Visualization

Build interactive charts, graphs, and dashboards that respond smoothly to user interactions.

🔧 Developer-Friendly API

Skia's declarative API feels natural to React developers, making complex graphics surprisingly approachable.

Getting Started with React Native Skia

First, install the library:

npm install @shopify/react-native-skia react-native-reanimated

# For iOS
cd ios && pod install
Enter fullscreen mode Exit fullscreen mode

Example 1: Creating a Smooth Animated Circle

Let's start with a simple but smooth animated circle that pulses:

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

export const AnimatedCircle = () => {
  const radius = useSharedValue(50);

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

  return (
    <Canvas style={{ flex: 1 }}>
      <Circle
        cx={150}
        cy={150}
        r={radius}
        color="lightblue"
      />
    </Canvas>
  );
};
Enter fullscreen mode Exit fullscreen mode

This creates a circle that smoothly grows and shrinks in an infinite loop – something that would cause performance issues with traditional Animated API at scale.

Example 2: Interactive Progress Ring

Here's a more practical example – an interactive progress ring that responds to touch:

import React, { useState } from 'react';
import { Canvas, Circle, Path, Skia } from '@shopify/react-native-skia';
import { Gesture, GestureDetector } from 'react-native-gesture-handler';
import { runOnJS } from 'react-native-reanimated';

interface ProgressRingProps {
  size?: number;
  strokeWidth?: number;
  color?: string;
}

const ProgressRing: React.FC<ProgressRingProps> = ({ size = 200, strokeWidth = 20, color = '#007AFF' }) => {
  const [progress, setProgress] = useState(0);
  const center = size / 2;
  const radius = center - strokeWidth / 2;

  const createArcPath = (progressValue: number) => {
    const path = Skia.Path.Make();
    const startAngle = -Math.PI / 2;
    const endAngle = startAngle + (progressValue * 2 * Math.PI);

    if (progressValue > 0) {
      path.addArc(
        { x: strokeWidth / 2, y: strokeWidth / 2, width: radius * 2, height: radius * 2 },
        (startAngle * 180) / Math.PI,
        ((endAngle - startAngle) * 180) / Math.PI
      );
    }
    return path;
  };

  const updateProgress = (value: number) => {
    setProgress(Math.max(0, Math.min(1, value)));
  };

  const panGesture = Gesture.Pan()
    .onUpdate((e) => {
      const x = e.x - center;
      const y = e.y - center;
      const angle = Math.atan2(y, x) + Math.PI / 2;
      const normalizedAngle = angle < 0 ? angle + 2 * Math.PI : angle;
      const progressValue = normalizedAngle / (2 * Math.PI);
      runOnJS(updateProgress)(progressValue);
    });

  return (
    <GestureDetector gesture={panGesture}>
      <Canvas style={{ width: size, height: size }}>
        <Circle
          cx={center}
          cy={center}
          r={radius}
          style="stroke"
          strokeWidth={strokeWidth}
          color="#E0E0E0"
        />
        {progress > 0 && (
          <Path
            path={createArcPath(progress)}
            style="stroke"
            strokeWidth={strokeWidth}
            strokeCap="round"
            color={color}
          />
        )}
      </Canvas>
    </GestureDetector>
  );
};

export {ProgressRing};
Enter fullscreen mode Exit fullscreen mode

Example 3: Real-Time Chart Animation

One of Skia's strongest use cases is creating smooth, interactive charts:

import React, { useEffect } from 'react';
import { Canvas, Path, Skia } from '@shopify/react-native-skia';
import { useSharedValue, withTiming, useDerivedValue, Easing } from 'react-native-reanimated';

const AnimatedLineChart = ({ data }: { data: number[] }) => {
  const progress = useSharedValue(0);

  useEffect(() => {
    progress.value = 0;
    progress.value = withTiming(1, { duration: 2000, easing: Easing.out(Easing.cubic) });
  }, [data]);

  const path = useDerivedValue(() => {
    const p = Skia.Path.Make();
    if (!data.length) return p;

    const max = Math.max(...data);
    const min = Math.min(...data);
    const range = max - min || 1;
    const totalProgress = progress.value * (data.length - 1);
    const completeSegments = Math.floor(totalProgress);
    const partialProgress = totalProgress - completeSegments;

    const getPoint = (i: number) => ({
      x: (i / (data.length - 1)) * 300,
      y: 200 - ((data[i] - min) / range) * 200
    });

    const start = getPoint(0);
    p.moveTo(start.x, start.y);

    for (let i = 1; i <= completeSegments && i < data.length; i++) {
      const curr = getPoint(i);
      const prev = getPoint(i - 1);
      const cp1x = prev.x + (curr.x - prev.x) * 0.4;
      const cp2x = prev.x + (curr.x - prev.x) * 0.6;
      p.cubicTo(cp1x, prev.y, cp2x, curr.y, curr.x, curr.y);
    }

    if (partialProgress > 0 && completeSegments + 1 < data.length) {
      const curr = getPoint(completeSegments + 1);
      const prev = getPoint(completeSegments);
      const endX = prev.x + (curr.x - prev.x) * partialProgress;
      const endY = prev.y + (curr.y - prev.y) * partialProgress;
      const cp1x = prev.x + (curr.x - prev.x) * 0.4 * partialProgress;
      const cp2x = prev.x + (curr.x - prev.x) * 0.6 * partialProgress;
      p.cubicTo(cp1x, prev.y, cp2x, endY, endX, endY);
    }

    return p;
  });

  return (
    <Canvas style={{ width: 300, height: 200 }}>
      <Path path={path} color="#2196F3" style="stroke" strokeWidth={4} strokeCap="round" />
    </Canvas>
  );
};

export default AnimatedLineChart;
// Usage
const chartData = [10, 25, 15, 40, 30, 55, 45, 60];
<AnimatedLineChart data={chartData} />
Enter fullscreen mode Exit fullscreen mode

Demo

Demo animation preview

Performance Benefits in Action

Traditional React Native animations often struggle with:

  • Frame drops during complex animations
  • JavaScript bridge bottlenecks when updating multiple elements
  • Limited styling options for custom graphics

React Native Skia solves these issues by:

  • Running animations on the UI thread at native speeds
  • Providing direct access to the graphics pipeline
  • Offering unlimited creative possibilities with custom shapes and effects

When to Use React Native Skia

Perfect for:

  • Custom charts and data visualizations
  • Complex animations and transitions
  • Image processing and filters
  • Games and interactive graphics
  • Custom UI components that need pixel-perfect control

Maybe overkill for:

  • Simple fade/slide animations
  • Standard UI components
  • Static layouts

Getting the Most Out of Skia

Performance Tips:

  1. Use shared values for animations instead of state
  2. Minimize re-renders by using useDerivedValue
  3. Batch updates when possible
  4. Profile your animations to identify bottlenecks

Best Practices:

  1. Start simple and build complexity gradually
  2. Leverage Skia's built-in optimizations
  3. Use gestures for interactive elements
  4. Cache complex calculations

The Future is Smooth

React Native Skia represents a fundamental shift in how we think about mobile app UI. By bringing native graphics performance to React Native, it opens up possibilities that were previously reserved for native development.

Whether you're building the next great data visualization app, creating engaging animations, or just want your app to feel incredibly smooth, React Native Skia provides the tools to make it happen – all while maintaining the developer experience you love about React Native.

The future of mobile UI is smooth, performant, and beautiful. With React Native Skia, that future is available today.


Ready to dive deeper? Check out the official React Native Skia documentation and start building your next smooth, stunning mobile experience.

Top comments (0)