Add Expo cookbook (SDK 53, React 19)#457
Merged
Merged
Conversation
New gl-react-cookbook-expo package with 5 examples: - Hello GL: basic UV gradient - Hello Blue: animated uniform - Color Disc: radial gradient with step() - Rotating Hello: shader composition - Motion Blur: backbuffering + composition Uses gl-react-expo with expo-gl. Configured with nodeLinker: node-modules for Metro/Expo compatibility with Yarn PnP monorepo. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
There was a problem hiding this comment.
Pull request overview
Adds a new Expo-based cookbook app under packages/cookbook-expo to showcase gl-react examples on iOS/Android using a modern Expo/React Native stack.
Changes:
- Introduces a new Expo app package with 5 shader demos (Hello GL, Hello Blue, Color Disc, Rotating Hello, Motion Blur).
- Adds Expo configuration (
app.json), TypeScript config, and an entrypoint registering the root component. - Adds required assets (icons/splash/favicon) and Yarn/ignore configuration for local development.
Reviewed changes
Copilot reviewed 7 out of 16 changed files in this pull request and generated 3 comments.
Show a summary per file
| File | Description |
|---|---|
| packages/cookbook-expo/tsconfig.json | Enables strict TS on top of Expo’s base config. |
| packages/cookbook-expo/package.json | Defines the Expo app package, scripts, and dependencies. |
| packages/cookbook-expo/index.ts | Expo entrypoint that registers the root component. |
| packages/cookbook-expo/gl-react.d.ts | Local TS module stubs for gl-react / gl-react-expo. |
| packages/cookbook-expo/app.json | Expo app configuration (name/slug, icons, Android adaptive icon, etc.). |
| packages/cookbook-expo/App.tsx | Implements the demo UI + shaders and demo registry. |
| packages/cookbook-expo/.yarnrc.yml | Attempts to enforce nodeLinker: node-modules for Expo/Metro. |
| packages/cookbook-expo/.gitignore | Ignores Expo/Metro/native build artifacts for the new package. |
| packages/cookbook-expo/assets/icon.png | App icon asset. |
| packages/cookbook-expo/assets/splash-icon.png | Splash image asset. |
| packages/cookbook-expo/assets/favicon.png | Web favicon asset. |
| packages/cookbook-expo/assets/android-icon-monochrome.png | Android monochrome adaptive icon asset. |
| packages/cookbook-expo/assets/android-icon-foreground.png | Android adaptive icon foreground asset. |
| packages/cookbook-expo/assets/android-icon-background.png | Android adaptive icon background asset. |
| .gitignore | Adds an ignore rule for the new package’s node_modules. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
- Remove unused useRef import - Fix single quotes to double quotes in index.ts - Add comment explaining nodeLinker placement - Add testID attributes to app components - Add Maestro smoke test flow (5 examples with screenshots) - Add e2e script to package.json Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- Add launchApp with deep link URL - Remove Motion Blur test (tab not visible without scroll) - Add extendedWaitUntil for app load - Note: requires Expo dev build (not Expo Go) for full CI automation Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- Add ios.bundleIdentifier for native dev build - Add metro.config.js for monorepo resolution - Simplify tab bar (View instead of ScrollView) for Maestro compat - Smoke test: verifies app launches and first GL example renders - CI workflow: prebuild → xcodebuild → simulator → Metro → Maestro - Tested locally: all assertions pass Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
The PnP monorepo install doesn't set up node_modules, so yarn workspace exec fails. Use npx babel directly which resolves via the root devDependencies. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
…les-core Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
… Xcode 16.4 Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
expo-modules-core has Swift 6 concurrency violations that are errors on Xcode 16.x. Setting minimal makes them warnings instead. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This tells the Swift compiler to treat concurrency violations as warnings, not errors, while keeping full Swift 6 language support (so @mainactor etc. still work). Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Only targets with SWIFT_VERSION=6.x get downgraded to 5.10. This keeps @mainactor working while treating concurrency violations as warnings instead of errors. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Instead of patching Podfile (which gets overridden by pod xcconfigs), directly sed Swift 6 → Swift 5 in all pod xcconfig files post-install. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Xcode 16.0 ships Swift 5.10 which treats expo-modules-core Swift 6 annotations as future features, not errors. Xcode 16.1+ ships Swift 6 which treats concurrency violations as errors. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
expo prebuild generates storyboard with toolsVersion from the local Xcode (26 beta), which is incompatible with Xcode 16.0 on CI. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- @preconcurrency import UIKit/Foundation in expo-modules-core Swift files - SWIFT_STRICT_CONCURRENCY=minimal in Pods pbxproj - Remove storyboard patch (not needed with default Xcode) Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
npx/yarn can't resolve react-native CLI through PnP. Use the local node_modules path from cookbook-expo instead. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Metro can't start in CI (PnP resolution issues). Instead, bundle the JS offline and copy it into the .app before installing. Also increase Maestro driver startup timeout to 120s. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Without this, Metro resolves entry-file from the monorepo root instead of the cookbook-expo project directory. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
expo export:embed resolves paths relative to the project directory correctly, unlike react-native CLI in a monorepo. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
npx resolves through root PnP which can't find expo. Use the local node_modules path directly. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
@expo/cli (hoisted to root by PnP) needs to resolve expo/package.json which lives in the local node_modules. NODE_PATH bridges the gap. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
getDefaultConfig doesn't always propagate projectRoot correctly when expo CLI is hoisted in a PnP monorepo. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Metro resolves --entry-file relative to the repo root rather than the working-directory, causing "Unable to resolve module ./index.ts" errors. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
gl-react unconditionally imports webgltexture-loader-ndarray which pulls
in ndarray, ndarray-ops, and typedarray-pool — Node.js packages that
crash Hermes at runtime ("Cannot read property 'S' of undefined").
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
React Native 0.79 (Expo SDK 53) accesses react.__CLIENT_INTERNALS_DO_NOT_USE_OR_WARN_USERS_THEY_CANNOT_UPGRADE which only exists in React 19. With React 18, this is undefined, causing "TypeError: Cannot read property 'S' of undefined" at runtime. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
RN 0.79.5 ships react-native-renderer 19.0.0 and requires an exact version match with the react package. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
The monorepo root may contain a different React version. Ensure Metro always resolves react, react-dom, and react-native from the cookbook-expo node_modules to prevent duplicate React copies in the bundle. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Use originModulePath trick to force react, react-dom, and react-native to always resolve from cookbook-expo's node_modules, preventing the monorepo root's React 18 from being bundled alongside React 19. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
There was a problem hiding this comment.
Pull request overview
Copilot reviewed 11 out of 20 changed files in this pull request and generated 5 comments.
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
Reads a 4x4 pixel sample from the center of the Surface via gl.readPixels() and displays "gl:rendered" or "gl:black" in a hidden text element. Maestro now asserts "gl:rendered" is visible. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
expo-gl clears the framebuffer after endFrameEXP(), so readPixels after the draw cycle returns zeros. Use Node's onDraw callback which fires right after gl.drawArrays() but before the buffer swap. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
readPixels returns zeros in expo-gl after buffer swap. Instead, just check that Node's onDraw fires — this callback runs after gl.drawArrays() succeeds, proving the shader compiled and drew. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- Remove unused imports (useRef, ScrollView) - Remove unused surfaceRef - Remove *.png from .gitignore (would hide committed assets) - Use yarn babel instead of npx babel in CI Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
yarn babel doesn't work in PnP — need yarn run -T to access top-level workspace binaries. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
New
packages/cookbook-expo— a modern Expo app showcasing gl-react on iOS/Android.5 examples:
Stack: Expo SDK 55, React 19, React Native 0.83, expo-gl, gl-react-expo
Note: Uses
nodeLinker: node-modulesin.yarnrc.ymlfor Metro/Expo compatibility with the Yarn PnP monorepo.Test plan
🤖 Generated with Claude Code