Skip to content

Add Expo cookbook (SDK 53, React 19)#457

Merged
gre merged 49 commits into
masterfrom
feat/cookbook-expo
Mar 22, 2026
Merged

Add Expo cookbook (SDK 53, React 19)#457
gre merged 49 commits into
masterfrom
feat/cookbook-expo

Conversation

@gre

@gre gre commented Mar 20, 2026

Copy link
Copy Markdown
Owner

Summary

New packages/cookbook-expo — a modern Expo app showcasing gl-react on iOS/Android.

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

Stack: Expo SDK 55, React 19, React Native 0.83, expo-gl, gl-react-expo

Note: Uses nodeLinker: node-modules in .yarnrc.yml for Metro/Expo compatibility with the Yarn PnP monorepo.

Test plan

  • TypeScript compiles clean
  • Tested on iOS Simulator (iPhone 17 Pro) — all 5 examples render correctly

🤖 Generated with Claude Code

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>
Copilot AI review requested due to automatic review settings March 20, 2026 19:10

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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.

Comment thread packages/cookbook-expo/App.tsx Outdated
Comment thread packages/cookbook-expo/index.ts Outdated
Comment thread packages/cookbook-expo/.yarnrc.yml
greweb and others added 26 commits March 20, 2026 20:24
- 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>
greweb and others added 16 commits March 21, 2026 20:52
- @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>

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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.

Comment thread packages/cookbook-expo/.gitignore Outdated
Comment thread packages/cookbook-expo/App.tsx Outdated
Comment thread .github/workflows/ci-cookbook-expo.yml
Comment thread packages/cookbook-expo/package.json
Comment thread .github/workflows/ci-cookbook-expo.yml
greweb and others added 4 commits March 22, 2026 19:18
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>
@gre gre changed the title Add modern Expo cookbook (SDK 55, React 19) Add Expo cookbook (SDK 53, React 19) Mar 22, 2026
greweb and others added 2 commits March 22, 2026 21:54
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>

@gre gre left a comment

Copy link
Copy Markdown
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

finally xd

@gre gre merged commit f0113aa into master Mar 22, 2026
3 checks passed
@gre gre deleted the feat/cookbook-expo branch June 24, 2026 11:44
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants