Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
17 changes: 11 additions & 6 deletions .github/workflows/build-npm.yml
Original file line number Diff line number Diff line change
Expand Up @@ -33,22 +33,27 @@ jobs:
run: yarn build

# Swap the prebuilt binary packages for their Graphite variants, pinned in
# graphiteDependencies (package.json). Only done at release time so that
# main and next stay identical.
# graphiteDependencies / graphiteOptionalDependencies (package.json). Only
# done at release time so that main and next stay identical.
- name: Swap in Graphite binary packages (next channel only)
if: github.ref_name == 'next'
working-directory: packages/skia
run: |
node -e "
const fs = require('fs');
const pkg = JSON.parse(fs.readFileSync('package.json', 'utf8'));
for (const name of Object.keys(pkg.dependencies)) {
if (name.startsWith('react-native-skia-')) delete pkg.dependencies[name];
for (const field of ['dependencies', 'optionalDependencies']) {
for (const name of Object.keys(pkg[field])) {
if (name.startsWith('react-native-skia-')) delete pkg[field][name];
}
}
for (const [src, dst] of [['graphiteDependencies', 'dependencies'], ['graphiteOptionalDependencies', 'optionalDependencies']]) {
if (!pkg[src] || Object.keys(pkg[src]).length === 0) throw new Error(src + ' is missing or empty');
Object.assign(pkg[dst], pkg[src]);
}
Object.assign(pkg.dependencies, pkg.graphiteDependencies);
fs.writeFileSync('package.json', JSON.stringify(pkg, null, 2) + '\n');
"
node -p "JSON.stringify(require('./package.json').dependencies, null, 2)"
node -p "const p = require('./package.json'); JSON.stringify({ dependencies: p.dependencies, optionalDependencies: p.optionalDependencies }, null, 2)"

- name: Build NPM Package
working-directory: packages/skia
Expand Down
53 changes: 48 additions & 5 deletions apps/docs/docs/getting-started/bundle-size.md
Original file line number Diff line number Diff line change
Expand Up @@ -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)).

Expand Down Expand Up @@ -38,6 +38,49 @@ 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.

### 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).
4 changes: 4 additions & 0 deletions apps/docs/docs/getting-started/installation.md
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,10 @@ 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.

The `react-native-skia-apple-*` packages are optional dependencies. Do not skip optional dependencies (`npm install --omit=optional` or an equivalent): that also drops the iOS binaries and `pod install` fails.

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.
Expand Down
10 changes: 7 additions & 3 deletions packages/skia/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -143,13 +143,17 @@
"dependencies": {
"canvaskit-wasm": "0.41.0",
"react-native-skia-android": "154.0.0",
"react-reconciler": "0.31.0"
},
"optionalDependencies": {
"react-native-skia-apple-ios": "154.0.0",
"react-native-skia-apple-macos": "154.0.0",
"react-native-skia-apple-tvos": "154.0.0",
"react-reconciler": "0.31.0"
"react-native-skia-apple-tvos": "154.0.0"
},
"graphiteDependencies": {
"react-native-skia-graphite-android": "154.0.0",
"react-native-skia-graphite-android": "154.0.0"
},
"graphiteOptionalDependencies": {
"react-native-skia-graphite-apple-ios": "154.0.0",
"react-native-skia-graphite-apple-macos": "154.0.0"
},
Expand Down
12 changes: 10 additions & 2 deletions packages/skia/react-native-skia.podspec
Original file line number Diff line number Diff line change
Expand Up @@ -114,13 +114,15 @@ 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/!"
Pod::UI.warn ""
Pod::UI.warn "Make sure dependencies are installed (yarn install / npm install) so that"
Pod::UI.warn "the #{expected_packages} packages are present, then run `pod install` again."
Pod::UI.warn "If you installed with --omit=optional (or an equivalent), these packages were"
Pod::UI.warn "skipped. Reinstall without it."
Pod::UI.warn "#{'-' * 72}"
raise "react-native-skia: Skia prebuilt binaries not found. Run `yarn install` then `pod install` to fix this."
end
Expand All @@ -129,7 +131,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'))
[]
Expand Down
7 changes: 7 additions & 0 deletions yarn.lock
Original file line number Diff line number Diff line change
Expand Up @@ -9122,6 +9122,13 @@ __metadata:
react-native: ">=0.78"
react-native-reanimated: ">=4.0.0"
react-native-worklets: ">=0.7.0"
dependenciesMeta:
react-native-skia-apple-ios:
optional: true
react-native-skia-apple-macos:
optional: true
react-native-skia-apple-tvos:
optional: true
peerDependenciesMeta:
react-native:
optional: true
Expand Down
Loading