From b0045239dfd14b551a1cc5ed1070f155007046f3 Mon Sep 17 00:00:00 2001 From: Spencer Yoder <25213226+Spencer-Yoder@users.noreply.github.com> Date: Fri, 14 Aug 2026 10:07:45 -0500 Subject: [PATCH 1/3] =?UTF-8?q?fix(=F0=9F=90=9B):=20don't=20require=20libs?= =?UTF-8?q?/macos=20to=20run=20pod=20install?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The podspec raises if `libs/macos` is missing, even when only iOS is being built. tvOS already handles its own absence gracefully a few lines below, so the two platforms behave inconsistently for the same condition. This matters for iOS-only apps. The `react-native-skia-apple-*` binaries are declared as hard `dependencies`, so every consumer downloads all of them. An iOS-only app can never use the macOS or tvOS xcframeworks, but pruning them with an override is blocked by the raise. Make macOS mirror the existing tvOS handling: fall back to an empty `vendored_frameworks` list when `libs/macos` is absent, and only hard-fail when `libs/ios` is missing. The error still fires for the case it was written for - a consumer who never ran `yarn install`, where `libs/ios` is also absent. No behaviour change when the packages are present, since libs/macos then exists and the original path is taken. Also document the npm-side story, which was previously a single sentence: which prebuilt packages exist, which can be pruned and how, and why react-native-skia-apple-ios, react-native-skia-android and canvaskit-wasm cannot. Clarifies that these packages affect node_modules and install time but not shipped app size, which is a recurring source of confusion. --- apps/docs/docs/getting-started/bundle-size.md | 61 ++++++++++++++++++- .../docs/docs/getting-started/installation.md | 2 + packages/skia/react-native-skia.podspec | 10 ++- 3 files changed, 69 insertions(+), 4 deletions(-) diff --git a/apps/docs/docs/getting-started/bundle-size.md b/apps/docs/docs/getting-started/bundle-size.md index 7e14cc74ae..6a1bcb559a 100644 --- a/apps/docs/docs/getting-started/bundle-size.md +++ b/apps/docs/docs/getting-started/bundle-size.md @@ -38,6 +38,63 @@ Unlike Android, there is no standard way to find the app size increase on iOS - Meaning that we’ve increased the size of our app by around 5,8 MB after adding React Native Skia. If we add the increased Javascript bundle of about 220 KB, we end up with about 6 MB of increased download size after including React Native Skia. -### NPM Package +## NPM Package -The NPM download is bigger than these numbers indicate because we need to distribute Skia for all target platforms on both iOS and Android. +The npm download is bigger than these numbers indicate because we need to distribute Skia for all target platforms on both iOS and Android. The prebuilt binaries ship as separate packages that `@shopify/react-native-skia` depends on: + +| Package | Needed for | Can be pruned? | +| ------- | ---------- | -------------- | +| `react-native-skia-apple-ios` | iOS | No | +| `react-native-skia-android` | Android | No | +| `react-native-skia-apple-macos` | macOS | Yes | +| `react-native-skia-apple-tvos` | tvOS | Yes | + +These affect the size of your `node_modules` and the time your installs and CI caches take — not the size of the app you ship. App size is determined by what actually gets linked, so an iOS-only app never ships the macOS or tvOS binaries either way. + +### Pruning unused platforms + +If you do want to keep them out of `node_modules`, redirect the unused packages to an empty local stub. Package managers cannot remove a dependency, but every one of them can override where it resolves from. + +Create `stubs/skia-apple-macos/package.json` in your app: + +```json +{ "name": "react-native-skia-apple-macos", "version": "0.0.0" } +``` + +That is the whole file — the version is required but is not checked, since overrides bypass range matching. Then point the dependency at it from your app's `package.json`: + +```json +{ + "overrides": { + "react-native-skia-apple-macos": "file:./stubs/skia-apple-macos" + } +} +``` + +The field name depends on your package manager: + +- **npm** and **Bun**: `overrides`, as above. +- **pnpm**: the same object, nested under `pnpm.overrides`. +- **Yarn Berry** (v2+): use `resolutions` with the `portal:` protocol instead of `file:`. + +Repeat for `react-native-skia-apple-tvos` if you don't build for Apple TV. + +Finally, remove any copy left behind by a previous install, otherwise the old frameworks are still found and you save nothing: + +```sh +rm -rf node_modules/@shopify/react-native-skia/libs/macos +``` + +Then run `pod install` again. + +:::info + +`patch-package` cannot do this. It runs in `postinstall`, after resolution and download have already happened, so editing the `dependencies` field that way frees no space. Only a resolution-level override prevents the fetch. + +::: + +### Why iOS and Android cannot be pruned + +Both are resolved during the native build and fail loudly when missing — CocoaPods raises if `libs/ios` is absent, and Gradle raises if `react-native-skia-android` cannot be resolved. Note that Gradle runs whenever your app has an `android/` directory, even if you never ship an Android build, so pruning the Android package will break your build rather than shrink it. + +`canvaskit-wasm` should also be left alone: it backs both the [web build](web) and the Jest mocks, so removing it breaks `yarn test` in apps that follow the [testing setup](installation#testing-with-jest). diff --git a/apps/docs/docs/getting-started/installation.md b/apps/docs/docs/getting-started/installation.md index 72453b04a8..3efa2c5305 100644 --- a/apps/docs/docs/getting-started/installation.md +++ b/apps/docs/docs/getting-started/installation.md @@ -26,6 +26,8 @@ npm install @shopify/react-native-skia The Skia prebuilt binaries are delivered as regular npm dependencies (`react-native-skia-android` and `react-native-skia-apple-*`) and are resolved automatically by the native build systems (CocoaPods on iOS/macOS/tvOS, Gradle on Android). No `postinstall` script is required, so there is nothing to allow or configure — `trustedDependencies` (Bun) or `enableScripts` (Yarn Berry) settings are not needed. +Every platform's binaries are downloaded, including ones your app may not target. This does not affect the size of the app you ship, but if you want to keep the unused ones out of `node_modules`, see [pruning unused platforms](bundle-size#pruning-unused-platforms). + ## Using Expo Expo provides a `with-skia` template, which you can use to create a new project. diff --git a/packages/skia/react-native-skia.podspec b/packages/skia/react-native-skia.podspec index 328ff30223..60eb24088b 100644 --- a/packages/skia/react-native-skia.podspec +++ b/packages/skia/react-native-skia.podspec @@ -114,7 +114,7 @@ framework_names += ['libwebgpu_dawn'] if use_graphite && !has_webgpu_pkg # Verify that the prebuilt binaries are available (copied in above from the npm # packages, or downloaded by install-skia-graphite for in-repo Graphite builds). -unless Dir.exist?(File.join(__dir__, 'libs', 'ios')) && Dir.exist?(File.join(__dir__, 'libs', 'macos')) +unless Dir.exist?(File.join(__dir__, 'libs', 'ios')) expected_packages = apple_skia_packages.values.join(', ') Pod::UI.warn "#{'-' * 72}" Pod::UI.warn "react-native-skia: Skia prebuilt binaries not found in libs/!" @@ -129,7 +129,13 @@ end # xcframeworks are copied into libs/ by install_apple_skia_libs above (default build) # or downloaded by install-skia-graphite (Graphite build). ios_frameworks = framework_names.map { |f| "libs/ios/#{f}.xcframework" } -osx_frameworks = framework_names.map { |f| "libs/macos/#{f}.xcframework" } +# macOS frameworks - check if libs/macos/ exists (mirrors the tvOS handling below, so that +# iOS-only consumers who prune react-native-skia-apple-macos can still run pod install) +osx_frameworks = if !Dir.exist?(File.join(__dir__, 'libs', 'macos')) + [] +else + framework_names.map { |f| "libs/macos/#{f}.xcframework" } +end # tvOS frameworks - check if libs/tvos/ exists (only populated for the default build) tvos_frameworks = if use_graphite || !Dir.exist?(File.join(__dir__, 'libs', 'tvos')) [] From df5ec3ce90247c5942fdc483348ba4de5156e0f9 Mon Sep 17 00:00:00 2001 From: Spencer Yoder <25213226+Spencer-Yoder@users.noreply.github.com> Date: Fri, 14 Aug 2026 10:15:21 -0500 Subject: [PATCH 2/3] Docs cleanup --- apps/docs/docs/getting-started/bundle-size.md | 32 ++++++------------- 1 file changed, 9 insertions(+), 23 deletions(-) diff --git a/apps/docs/docs/getting-started/bundle-size.md b/apps/docs/docs/getting-started/bundle-size.md index 6a1bcb559a..fc83a53e4d 100644 --- a/apps/docs/docs/getting-started/bundle-size.md +++ b/apps/docs/docs/getting-started/bundle-size.md @@ -7,9 +7,9 @@ slug: /getting-started/bundle-size Below is the app size increase to be expected when adding React Native Skia to your project. -| Apple | Android | Web | -|----------|--------------| -------- | -| 6 MB | 4 MB | 2.9 MB\* | +| Apple | Android | Web | +| ----- | ------- | -------- | +| 6 MB | 4 MB | 2.9 MB\* | \*This figure is the size of the gzipped file served through a CDN ([learn more](web)). @@ -42,12 +42,12 @@ Meaning that we’ve increased the size of our app by around 5,8 MB after adding The npm download is bigger than these numbers indicate because we need to distribute Skia for all target platforms on both iOS and Android. The prebuilt binaries ship as separate packages that `@shopify/react-native-skia` depends on: -| Package | Needed for | Can be pruned? | -| ------- | ---------- | -------------- | -| `react-native-skia-apple-ios` | iOS | No | -| `react-native-skia-android` | Android | No | -| `react-native-skia-apple-macos` | macOS | Yes | -| `react-native-skia-apple-tvos` | tvOS | Yes | +| Package | Needed for | Can be pruned? | +| ------------------------------- | ---------- | -------------- | +| `react-native-skia-apple-ios` | iOS | No | +| `react-native-skia-android` | Android | No | +| `react-native-skia-apple-macos` | macOS | Yes | +| `react-native-skia-apple-tvos` | tvOS | Yes | These affect the size of your `node_modules` and the time your installs and CI caches take — not the size of the app you ship. App size is determined by what actually gets linked, so an iOS-only app never ships the macOS or tvOS binaries either way. @@ -79,20 +79,6 @@ The field name depends on your package manager: Repeat for `react-native-skia-apple-tvos` if you don't build for Apple TV. -Finally, remove any copy left behind by a previous install, otherwise the old frameworks are still found and you save nothing: - -```sh -rm -rf node_modules/@shopify/react-native-skia/libs/macos -``` - -Then run `pod install` again. - -:::info - -`patch-package` cannot do this. It runs in `postinstall`, after resolution and download have already happened, so editing the `dependencies` field that way frees no space. Only a resolution-level override prevents the fetch. - -::: - ### Why iOS and Android cannot be pruned Both are resolved during the native build and fail loudly when missing — CocoaPods raises if `libs/ios` is absent, and Gradle raises if `react-native-skia-android` cannot be resolved. Note that Gradle runs whenever your app has an `android/` directory, even if you never ship an Android build, so pruning the Android package will break your build rather than shrink it. From 26986e59f020cdedd7dcb097692f079763e852dc Mon Sep 17 00:00:00 2001 From: Spencer Yoder <25213226+Spencer-Yoder@users.noreply.github.com> Date: Fri, 14 Aug 2026 10:23:49 -0500 Subject: [PATCH 3/3] =?UTF-8?q?chore(=F0=9F=90=99):=20rerun=20CI?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Empty commit to re-trigger the workflow jobs. Co-Authored-By: Claude Opus 5