Skip to content

Duplicate WebGPUView registration when using react-native-skia alongside react-native-wgpu #3794

@JeanDes-Code

Description

@JeanDes-Code

Description

Description

When both @shopify/react-native-skia (v2.5.4) and react-native-wgpu (v0.5.9) are installed in the same project, the iOS build fails at the linker stage with 3 duplicate ObjC symbols (_OBJC_METACLASS_$_WebGPUView, _OBJC_CLASS_$_WebGPUView, WebGPUViewCls()). The project cannot be built at all.

This happens because both packages register a native component called WebGPUView through three independent mechanisms:

1. codegenConfig in package.json

Both packages declare WebGPUView in their codegenConfig.ios.componentProvider:

@shopify/react-native-skia/package.json:

"codegenConfig": {
  "name": "rnskia",
  "ios": {
    "componentProvider": {
      "SkiaPictureView": "SkiaPictureView",
      "WebGPUView": "WebGPUView"
    }
  }
}

react-native-wgpu/package.json:

"codegenConfig": {
  "name": "RNWgpuViewSpec",
  "ios": {
    "componentProvider": {
      "WebGPUView": "WebGPUView"
    }
  }
}

The React Native codegen processes both during prebuild and generates duplicate native registrations.

2. JS codegen specs

Both packages have a WebGPUViewNativeComponent.js that calls codegenNativeComponent("WebGPUView"):

  • @shopify/react-native-skia/lib/module/specs/WebGPUViewNativeComponent.js
  • react-native-wgpu/lib/module/WebGPUViewNativeComponent.js

3. Native ObjC files

Both packages ship their own WebGPUView.mm and WebGPUView.h, which are both compiled and linked into the final binary:

  • libreact-native-skia.a contains WebGPUView.o
  • libreact-native-wgpu.a contains WebGPUView.o

Skia's podspec does not exclude these files in v2.5.4, causing the 3 duplicate linker symbols.

Use case

We use react-native-wgpu as a peer dependency of typegpu-confetti, which imports Canvas, useCanvasRef, and useDevice directly from react-native-wgpu. We can't remove it.

Workaround

We applied a patch-package patch to @shopify/react-native-skia that:

  1. Removes WebGPUView from codegenConfig.ios.componentProvider in package.json — stops the codegen from generating duplicate native registrations
  2. Excludes apple/WebGPUView.mm and apple/WebGPUView.h in the podspec — prevents duplicate native ObjC class compilation
  3. Redirects the JS specs (lib/module/specs/WebGPUViewNativeComponent.js and lib/commonjs/...) to re-export from react-native-wgpu instead of calling codegenNativeComponent directly
  4. Redirects the TS source spec (src/specs/WebGPUViewNativeComponent.ts) similarly

After patching, a clean prebuild + native build resolves the crash.

Full patch file
diff --git a/node_modules/@shopify/react-native-skia/package.json b/node_modules/@shopify/react-native-skia/package.json
--- a/node_modules/@shopify/react-native-skia/package.json
+++ b/node_modules/@shopify/react-native-skia/package.json
@@ -147,8 +147,7 @@
     },
     "ios": {
       "componentProvider": {
-        "SkiaPictureView": "SkiaPictureView",
-        "WebGPUView": "WebGPUView"
+        "SkiaPictureView": "SkiaPictureView"
       }
     }
   },

diff --git a/node_modules/@shopify/react-native-skia/lib/module/specs/WebGPUViewNativeComponent.js b/node_modules/@shopify/react-native-skia/lib/module/specs/WebGPUViewNativeComponent.js
--- a/node_modules/@shopify/react-native-skia/lib/module/specs/WebGPUViewNativeComponent.js
+++ b/node_modules/@shopify/react-native-skia/lib/module/specs/WebGPUViewNativeComponent.js
@@ -1,4 +1,2 @@
-import codegenNativeComponent from "react-native/Libraries/Utilities/codegenNativeComponent";
-// eslint-disable-next-line import/no-default-export
-export default codegenNativeComponent("WebGPUView");
+// Re-export from react-native-wgpu to avoid duplicate registration
+export { default } from "react-native-wgpu/lib/module/WebGPUViewNativeComponent";

diff --git a/node_modules/@shopify/react-native-skia/lib/commonjs/specs/WebGPUViewNativeComponent.js b/node_modules/@shopify/react-native-skia/lib/commonjs/specs/WebGPUViewNativeComponent.js
--- a/node_modules/@shopify/react-native-skia/lib/commonjs/specs/WebGPUViewNativeComponent.js
+++ b/node_modules/@shopify/react-native-skia/lib/commonjs/specs/WebGPUViewNativeComponent.js
@@ -1,11 +1,8 @@
 "use strict";

+// Re-export from react-native-wgpu to avoid duplicate registration
 Object.defineProperty(exports, "__esModule", {
   value: true
 });
-exports.default = void 0;
-var _codegenNativeComponent = _interopRequireDefault(require("react-native/Libraries/Utilities/codegenNativeComponent"));
-function _interopRequireDefault(e) { return e && e.__esModule ? e : { default: e }; }
-// eslint-disable-next-line import/no-default-export
-var _default = exports.default = (0, _codegenNativeComponent.default)("WebGPUView");
+var _wgpu = require("react-native-wgpu/lib/commonjs/WebGPUViewNativeComponent");
+exports.default = _wgpu.default;

diff --git a/node_modules/@shopify/react-native-skia/src/specs/WebGPUViewNativeComponent.ts b/node_modules/@shopify/react-native-skia/src/specs/WebGPUViewNativeComponent.ts
--- a/node_modules/@shopify/react-native-skia/src/specs/WebGPUViewNativeComponent.ts
+++ b/node_modules/@shopify/react-native-skia/src/specs/WebGPUViewNativeComponent.ts
@@ -1,11 +1,2 @@
-import codegenNativeComponent from "react-native/Libraries/Utilities/codegenNativeComponent";
-import type { Int32 } from "react-native/Libraries/Types/CodegenTypes";
-import type { ViewProps } from "react-native";
-
-export interface NativeProps extends ViewProps {
-  contextId: Int32;
-  transparent: boolean;
-}
-
-// eslint-disable-next-line import/no-default-export
-export default codegenNativeComponent<NativeProps>("WebGPUView");
+// Re-export from react-native-wgpu to avoid duplicate registration
+export { default } from "react-native-wgpu/src/WebGPUViewNativeComponent";

diff --git a/node_modules/@shopify/react-native-skia/react-native-skia.podspec b/node_modules/@shopify/react-native-skia/react-native-skia.podspec
--- a/node_modules/@shopify/react-native-skia/react-native-skia.podspec
+++ b/node_modules/@shopify/react-native-skia/react-native-skia.podspec
@@ -97,7 +97,9 @@
     'cpp/rnwgpu/**/*.{h,cpp}',
     'cpp/jsi2/**/*.{h,cpp}'
   ]
-  s.exclude_files = graphite_exclusions unless use_graphite
+  # Exclude WebGPUView — conflicts with react-native-wgpu which provides the canonical impl.
+  webgpu_view_exclusions = ['apple/WebGPUView.mm', 'apple/WebGPUView.h']
+  s.exclude_files = (use_graphite ? [] : graphite_exclusions) + webgpu_view_exclusions

   if defined?(install_modules_dependencies()) != nil
     install_modules_dependencies(s)

Suggested fix

When react-native-wgpu is installed alongside Skia, Skia should defer to it as the canonical WebGPUView provider. This requires changes at all three levels:

  • Podspec — exclude apple/WebGPUView.mm and apple/WebGPUView.h to avoid duplicate native classes
  • codegenConfig.ios.componentProvider — remove WebGPUView to avoid duplicate codegen registrations
  • JS/TS specs — re-export from react-native-wgpu instead of calling codegenNativeComponent directly

React Native Skia Version

2.5.4

React Native Version

0.83.4

Using New Architecture

  • Enabled

Steps to Reproduce

  1. Clone the repro repo
  2. npm install --legacy-peer-deps && npx install-skia
  3. npx expo prebuild --clean
  4. npx expo run:ios

Result: Build fails at the linker stage with 3 duplicate ObjC symbols — the project cannot be built at all.

Build output:

❌  duplicate symbol '_OBJC_METACLASS_$_WebGPUView' in
┌─ libreact-native-skia.a[28](WebGPUView.o)
└─ libreact-native-wgpu.a[32](WebGPUView.o)

❌  duplicate symbol '_OBJC_CLASS_$_WebGPUView' in
┌─ libreact-native-skia.a[28](WebGPUView.o)
└─ libreact-native-wgpu.a[32](WebGPUView.o)

❌  duplicate symbol 'WebGPUViewCls()' in
┌─ libreact-native-skia.a[28](WebGPUView.o)
└─ libreact-native-wgpu.a[32](WebGPUView.o)

❌  ld: 3 duplicate symbols
❌  clang: error: linker command failed with exit code 1

Snack, Code Example, Screenshot, or Link to Repository

https://github.com/JeanDes-Code/RNSkia-dualWebGPUView-minimal-repro

Metadata

Metadata

Assignees

No one assigned

    Labels

    bugSomething isn't workingreleased

    Type

    No type
    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions