agentsclimarketplace

Expo react native app

Skill POWR-DATA/mtx-skills/skills/app/expo-react-native-app

Reusable AI agent skills for application development, data, architecture, domain modelling and delivery workflows.

Install
npx -y skills add POWR-DATA/mtx-skills --skill expo-react-native-app

Assembled from the repository path, not quoted from the project. Check it against their README if it does not work.

One thing to look at

  • 0 stars0 stars. Stars are a popularity signal and not a quality one, but at this level it is likely that nobody has read this closely except its author, and you would be relying on your own review.

What its author says it does

Copied from the file, not written here

Build cross-platform React Native apps with Expo — correct setup patterns, cross-platform gotchas, and hard-won lessons from native and web targets

The file declares its own license as MIT. That is the author’s claim about this one file, and it is not the same thing as the license GitHub reports for the repository, which is listed with the other numbers below.

SKILL.md

13.2 KB, as published. Nobody here has run it

Expo React Native App

Purpose

Guide the setup and development of a cross-platform React Native app using Expo, covering icon and font configuration, Metro bundler behaviour, web-platform API access, and UI component gotchas that only surface on specific targets or themes.

When to use

When starting a new Expo project or debugging a problem that appears only on a specific platform (web, dark theme, after a package install). Apply at project setup time to avoid the class of silent failures that only surface on device or in the browser target.

Inputs expected

  • Expo SDK version and target platforms (native, web, or both)
  • Icon set or UI library in use (e.g. @expo/vector-icons, custom assets)
  • External API endpoints the app will call
  • Known errors or unexpected rendering behaviour

Guiding principles

  • Vector icon fonts must be explicitly preloaded. @expo/vector-icons (Ionicons and others) renders blank boxes unless useFonts(Iconset.font) is called in the root layout and rendering is gated behind fontsLoaded. This is required even when the package is already installed — installation does not trigger font registration.
  • Clear Metro cache after every package install. After npx expo install <package>, run npm start -- --clear or npx expo start --clear. A plain restart leaves stale module resolution and causes icons or modules to fail silently until the cache is cleared.
  • Proxy all external API calls — never branch by platform for CORS. External REST API calls that work on native are silently blocked by CORS on Expo web. The fix is a proxy (e.g. a Supabase Edge Function) so both native and web use the same code path. A web-only fetch branch doubles the maintenance surface and will diverge.
  • Emoji ignores colour styling in TextInput rows. Emoji characters inside TextInput rows are invisible on dark backgrounds because emoji rendering ignores the color style prop. Use a short text label (SHOW/HIDE) with an explicit color instead. Merge the label button into a shared bordered container using flexDirection: 'row' to avoid an unwanted internal dividing line.
  • Test on both native and web targets before considering a screen complete. Behaviour differences between native and web (CORS, emoji rendering, font loading) only surface when the target is exercised — do not rely on native-only testing.
  • Expo Go is no longer available for SDK 56+ — use EAS development builds. Expo Go was removed from the App Store for SDK 56 (confirmed May 2026). The correct local testing approach for iOS is an EAS development build with expo-dev-client, including when developing on Windows against a physical device.
  • The iOS icon field in app.config.js must point to a real PNG. The Expo default template may leave a placeholder (e.g. ./assets/expo.icon) that is not a valid image. Replace it with a real PNG path (e.g. ./assets/images/icon.png) before building, or the iOS build fails.
  • Use a Modal-based dropdown when a list sits inside nested ScrollViews on Android. A ScrollView inside an absolutely-positioned View that is itself inside a parent ScrollView cannot receive scroll touches — the parent clips touch events regardless of nestedScrollEnabled. The reliable fix: measureInWindow on the trigger ref to get screen coordinates, then render the list in a Modal positioned at those coordinates, placing it entirely outside the ScrollView hierarchy.
  • iOS Modal components ignore the app-level orientation lock. Expo's orientation: 'portrait' config does not propagate to modals. Each Modal must explicitly declare supportedOrientations={['portrait']} or it rotates when the device is held sideways or in Stage Manager on iPad.
  • On Windows, the Android emulator lives at C:\Users\<username>\Android\sdk\emulator\emulator.exe — not %LOCALAPPDATA%\Android\Sdk\emulator. Multiple expo start invocations accumulate Metro instances across ports 8081, 8083, 8085…; kill all processes on ports 8081–8090 before starting a new Metro instance, then run adb reverse tcp:8081 tcp:8081 so the emulator can reach Metro on the host.
  • Expo/React Native and Flet/Flutter are entirely separate stacks — no patterns transfer between them. Expo uses TypeScript → React Native → native platform UI (UIKit on iOS, Jetpack Compose on Android). Flet uses Python → Flutter (Dart) → canvas-based rendering. No runtime is shared; packages, debugging approaches, and build tooling are completely different.
  • Choose Expo over Flet for production mobile unless Python familiarity is the only constraint. Expo's hot reload dev cycle, EAS build/signing/OTA tooling, native push notification support, and ecosystem size substantially outweigh Flet's sole advantage of Python. If the team is comfortable with TypeScript, choose Expo.
  • Expo only exposes EXPO_PUBLIC_* env vars to client code. app.config.js extra must read the prefixed names (e.g. process.env.EXPO_PUBLIC_SUPABASE_URL), and CI workflows must inject under those names too. Reading the unprefixed SUPABASE_URL yields empty values and a runtime "supabaseUrl is required" crash.
  • app.config.js extra constants are Metro-cached. After editing app.config.js, the old values persist until you restart with npx expo start --clear. A stale-config error that won't go away is usually this, not a code bug.
  • Consolidate per-app placeholders into one template.config.json + a generator script. The generator stamps app.config.js / package.json / eas.json / the CI workflow and regenerates a single src/constants/AppConfig.ts the screens import — killing hardcoded URL/name sprawl and making a new app one edit plus one command.
  • Push notifications require a development build, not Expo Go. Expo push was removed from Expo Go in SDK 53 (deprecated in 52) and only works in a dev build (expo-dev-client). The iOS Simulator cannot receive push (physical device required); EAS provisions the APNs/FCM credentials.
  • Native in-app review is a silent no-op on sideloaded/dev builds. expo-store-review (Play In-App Review / SKStoreReviewController) only renders on store-distributed installs (Android: any Play track, Internal testing is enough; iOS: TestFlight/App Store). On a dev APK requestReview() does nothing — that is correct, not a bug. Verify it as a launch-time test on a real store track; never add a dev-only fallback to force it.
  • Lazy-load optional native modules with a synchronous require(). Importing an optional native module (e.g. expo-store-review) at the top level crashes any dev build whose native binary predates the JS module ("Cannot find native module"); await import() then trips a Metro async-require bug ("Requiring unknown module N"). Use a synchronous require() inside the function (with an eslint-disable for no-require-imports) to keep graceful degradation.
  • Reach Metro from a physical Android device over USB with adb reverse. Run adb reverse tcp:8081 tcp:8081, start with plain npx expo start --dev-client (default 0.0.0.0 binding), and in the dev launcher enter http://localhost:8081. Do not pass --localhost — it binds Metro to IPv6 ::1 only, breaking the IPv4 adb-reverse tunnel ("unexpected end of stream" / UNAUTHORIZED).
  • On Windows under OneDrive, mark node_modules "Always keep on this device". OneDrive's on-demand placeholder files make Metro bundling fail with readlink EINVAL until the files are materialised locally.
  • Scope App Links / Universal Links to the landing path only (/<slug>, /<slug>/), never /<slug>/*. Broadening the pattern hijacks web-only auth pages (e.g. /<slug>/reset-password, /<slug>/confirm-email) into the app and breaks them — those must complete in the browser. Links also stay inert until assetlinks.json (Android, signing SHA-256) / AASA (iOS, Apple Team ID) are deployed to /.well-known/ and the app is rebuilt.
  • When the website's URL path differs from the Expo slug, use a separate web-path value. The slug is a technical id tied to the EAS project — don't derive web URLs or App-Link paths from it. Introduce webSlug / extra.webPath for the web path and keep the slug for builds/credentials; +native-intent.tsx should strip extra.webPath, not Constants.expoConfig.slug, so the landing link resolves to the app root even when slug ≠ URL segment.

Process

  1. Set up font preloading in the root layout. For every icon set in use, call useFonts(Iconset.font) at the root layout level. Gate the return of any component tree behind if (!fontsLoaded) return null. Do not render icon-dependent components before fontsLoaded is true.
  2. Install packages via npx expo install. This selects the SDK-compatible version. After every install, restart with --clear to flush Metro's module cache.
  3. Route all external API calls through a proxy. Create a Supabase Edge Function (or equivalent server-side proxy) that forwards requests to the external API. Call the proxy from both native and web — do not use Platform.OS branching for fetch logic.
  4. Audit interactive UI for dark-theme compatibility. Check any component that uses emoji, icon characters, or colour-reliant elements against a dark background. Replace emoji with text labels or @expo/vector-icons components that respect the color prop.
  5. Smoke test on web target. Run npx expo start --web and exercise all screens. CORS errors and font failures that pass on native will surface here.

Output format

  1. Setup checklist — font preloading wired, Metro cache strategy confirmed, proxy endpoint identified
  2. Screen-by-screen notes — any platform-specific issues found and how they were resolved
  3. Remaining work — items that need device testing beyond what the web target covers

Quality checklist

  • useFonts(Iconset.font) called for every icon set in the root layout
  • Rendering gated on fontsLoaded before any icon-dependent component renders
  • Metro restarted with --clear after every new package install
  • All external API calls routed through a proxy — no Platform.OS branching for fetch
  • Interactive TextInput components use text labels, not emoji, for dark-theme compatibility
  • Web target tested with npx expo start --web
  • iOS icon in app.config.js points to a real PNG, not a template placeholder
  • iOS testing uses an EAS development build (expo-dev-client), not Expo Go, on SDK 56+
  • Portrait-locked iOS modals declare supportedOrientations={['portrait']}
  • Nested-ScrollView dropdowns on Android use a Modal rather than an inner ScrollView
  • Client env vars use the EXPO_PUBLIC_* prefix in app.config.js extra and in CI
  • Push notifications, in-app review, and other store-only features tested on a real store track — not a dev build
  • Optional native modules lazy-loaded via synchronous require(), not top-level or await import()
  • App Links / Universal Links scoped to the landing path only, with assetlinks.json/AASA deployed

Avoid

  • Assuming vector icons work without useFonts — they will render as blank boxes
  • Restarting Metro without --clear after package installs — stale cache causes silent module failures
  • Adding a web-only fetch branch for CORS — use a proxy that works on both targets
  • Using emoji in TextInput interactive elements — emoji ignores color and disappears on dark backgrounds
  • Testing only on the native simulator — web-specific failures (CORS, font loading) only surface in the browser target
  • Applying Flet/Flutter patterns, packages, or mental models to Expo development — they are entirely separate stacks with no shared runtime
  • Relying on Expo Go for SDK 56+ iOS testing — it is no longer available; use an EAS development build with expo-dev-client
  • Leaving the Expo template's placeholder icon path in app.config.js — point the iOS icon field at a real PNG before building
  • Nesting a scrollable list inside an absolutely-positioned View within a parent ScrollView on Android — touch events are clipped; render the list in a Modal instead
  • Omitting supportedOrientations={['portrait']} on iOS modals when the app is portrait-locked — modals rotate independently of the app config
  • Reading unprefixed env vars (SUPABASE_URL) in client code — only EXPO_PUBLIC_* names reach the client; the rest are empty at runtime
  • Top-level importing an optional native module — it crashes dev builds whose native binary predates it; lazy-load with require()
  • Broadening App Links to /<slug>/* — it hijacks web-only auth pages into the app; scope to the landing path only
  • Passing --localhost to expo start for USB device debugging — it binds IPv6-only and breaks the adb-reverse tunnel

Example usage

Starting a new Expo app — icons blank on first run, API calls working on iOS but failing on web, password toggle invisible in dark mode


Source: This skill is sourced from the Matrix Skills library. Learn more at the AI Agent Skills Library.

Keep looking

Skills are one crate of 328,083. Ordering is by how many stacks a row turns up in, so the top of any crate is what has actually been picked rather than what has the most stars.