Setup React Native App

This documentation was generated with the assistance of AI. Please report any inaccuracies.

Scaffold a new React Native app (New Architecture only, Cordova-free) on one of two toolchains, chosen with the toolchain argument. expo runs create-expo-app’s `blank-typescript template (a managed Expo app); metro runs the React Native Community CLI’s default TypeScript template (a bare app with committed android/ and ios/ projects). In both cases this catalog’s standard test, lint, build and Sonar tooling is layered on top using explicit example templates kept in the skill’s reference/ directory.

Purpose

iru-setup-react-native-app bootstraps a React Native app’s scaffold plus its tooling, per toolchain:

  • Expo (reference/expo-templates.md) — a jest-expo + @testing-library/react-native test setup, an eslint-config-expo flat config (generated by npx expo lint), Prettier, an eas.json with development/preview/production build profiles, and a native-builds choice (eas or local). No committed ios//android/ folders; native projects are generated on demand (Continuous Native Generation).

  • Metro (reference/metro-templates.md) — the template’s committed android/ (Gradle) and ios/ (Xcode
    CocoaPods) projects, React Navigation instead of Expo Router, no EAS. The bundle identifier (app-id) is required up front, identity values the CLI gets wrong are fixed, the template’s @react-native/jest-preset, .eslintrc.js and .prettierrc.js are kept, and React Native Testing Library, lcov coverage with a safe-area mock, the shared test/coverage/lint/format/typecheck scripts and ESLint/Prettier ignores for native build output are added. The skill then builds both native projects (./gradlew assembleDebug, and on macOS bundle exec pod install plus a simulator xcodebuild), marking whatever cannot run locally as unverified.

Both toolchains also get a Maestro smoke flow (.maestro/smoke.yaml), the catalog’s UI contract wiring (see Building user interfaces; templates in reference/ui-templates.md) and, when opted in, sonar-project.properties (TypeScript only; native sources are excluded). Expo is also bundled with Metro under the hood — the argument names the two toolchains a user actually chooses between. Only the reference file for the resolved toolchain is loaded, plus reference/ui-templates.md for both. A project whose detected toolchain disagrees with the toolchain argument is warned about and asked; the skill never converts an app between toolchains. For a Capacitor-based hybrid app, Setup Ionic App is used instead.

The UI contract wiring is: .ui-quality.yml at the repository root (accessibility: enforce|report|off|none, directions, themes); eslint-plugin-react-native-a11y in the ESLint config at that level (React Native has no automated native contrast or touch-target audit, so the check is that lint plus the tests' role/label queries); a dev-only UI gallery (src/dev/ui-gallery/, reached by a <app-slug>://ui-gallery deep link and stripped from production bundles by DEV) with its Jest test; and the Maestro screenshot helper .maestro/subflows/ui-capture.yaml plus a capture flow for the root screen that writes .ui-screenshots/screen/App/default--<ltr|rtl>--<light|dark>--<platform>.png. On a new scaffold the entry file (index.ts or index.js) wraps the app with the gallery and the deep-link scheme is registered; on an existing app those edits are printed for the user instead.

Plugin

Shipped by the iru-react-native plugin of the ai-catalog marketplace (install with /plugin install iru-react-native@ai-catalog; see Plugins).

Invocation

/iru-setup-react-native-app

Inputs

Argument Required Description Default

args

No

Pre-resolved parameters passed by an orchestrating skill (e.g. Setup TypeScript Repository) as newline-separated key: value lines. Every field found here is resolved in Step 0 and skipped in Step 2.

None — if absent or unrecognized, every field is asked interactively.

toolchain

Yes (unless supplied via args or detected)

expo (recommended for most new apps) or metro (bare React Native CLI project). Asked first with AskUserQuestion. An existing app’s detected toolchain is adopted; a disagreeing value triggers a warning and a choice between the detected toolchain and stopping.

None — asked, or detected from an existing package.json

app-name

Yes (unless supplied via args)

Display name: app.json’s `expo.name (expo) or displayName, Android app_name and iOS CFBundleDisplayName (metro).

None

app-slug

No

Kebab-case slug written to package.json’s `name (and, for expo, app.json’s `expo.slug).

A slugified app-name

app-id

Required for metro; optional for expo

Reverse-DNS bundle identifier / Android application id (e.g. com.example.myapp). For metro it is baked into applicationId, the Kotlin package and PRODUCT_BUNDLE_IDENTIFIER; for expo it is written to expo.ios.bundleIdentifier/expo.android.package and the Maestro flow when given.

Suggested from organization-url + app-slug; unset for expo if not given

app-directory

No

Subdirectory to scaffold into (or . if the repository root is empty of non-dotfiles).

Resolved app-slug, confirmed with the user

license

Yes (unless supplied via args)

Resolved via AskUserQuestion: MIT License, Apache License 2.0, No license, or Other.

None — user must choose

developer-name / developer-email / organization-url

Yes (unless supplied via args)

Developer identity fields recorded in the report; no build file uses them directly.

None

open-source

No

Whether the repository is open source (yes/no) — drives the sonar recommendation together with visibility.

Asked first when invoked stand-alone

sonar (+ sonar-organization/sonar-project-key/sonar-host-url)

No

Whether to wire up SonarQube/SonarCloud (cloud/self-hosted/none), plus its organization/project key/host URL. cloud is recommended only for a repository that is both public and open source (SonarCloud is free only for public open-source projects, per its current pricing); otherwise none is recommended, with self-hosted SonarQube as the alternative.

cloud if public and open source, else none recommended

native-builds

No (expo only)

How native builds run: eas (Expo’s cloud, 15 builds/platform/month free) or local (eas build --local/expo prebuild). Never asked for metro, and ignored with a one-line note if passed.

eas

distribution

No

none, internal, or store. Expo: gates eas.json’s `submit block and the store-credential secrets named in the report. Metro (no EAS): names the Android upload keystore, Google Play and App Store Connect secrets the release workflow will need.

none/internal/store per this catalog’s shared convention

accessibility

No

enforce, report, off or none — whether accessibility support is wanted and the UI contract’s automated level, written to .ui-quality.yml and applied to eslint-plugin-react-native-a11y (errors, warnings or off). none opts out entirely: accessibility: none is written to .ui-quality.yml, no accessibility test tooling (the plugin and role-query assertions) is installed and the accessibility attributes are omitted from the templates. Otherwise the templates carry the assistive-technology attributes. Asked in stand-alone runs when args did not supply it.

enforce; an existing .ui-quality.yml pre-selects its value

visibility

No

public, private, or internal — the GitHub repository’s visibility, used only to key the sonar recommendation. internal counts as private. If open-source: yes but visibility is private/internal, the skill warns and asks which is right.

Resolved only when the sonar question is asked: gh repo view --json visibility -q .visibility, else asked (Public recommended when open source)

ci

No

yes/no — whether the run invoking this skill generates GitHub Actions workflows at all; consulted only by the sonar question. With ci: no, none is recommended for Sonar since no CI would send reports (sonar-project.properties is still written if the user picks cloud/self-hosted, and the report says so).

yes implied

on-existing

No

ask/keep/update/replace — how Step 1 treats each pre-existing tooling file. keep/update/replace is applied to every one without asking (the warning is still printed); meant for scripted runs.

ask

mode

No

new or existing. existing marks the run as an update of an established repository, but never means "update silently" — Step 1 still warns and asks per pre-existing tooling file unless on-existing says otherwise.

new

Outputs

  • A scaffolded app at app-directory — Expo via create-expo-app@latest --template blank-typescript, or Metro via @react-native-community/cli@latest init (committed android/ and ios/ projects, the template’s own README.md and .gitignore inside app-directory) — or only the missing tooling files when gap-filling an existing scaffold. Application source is never touched.

  • package.json merged with test/coverage/lint/format/typecheck scripts and this skill’s devDependencies (metro keeps the template’s own pins for jest, eslint 8 and prettier 2.8.8), with version 0.1.0-dev.0 on new scaffolds.

  • Expo: jest.config.js, a tsconfig.json merge (types: ["jest"]), tests/App.test.tsx, eslint.config.js (generated by npx expo lint), .prettierrc/.prettierignore, and eas.json with development/preview/production build profiles (plus a submit.production block when distribution: store).

  • Metro: identity fix-ups (package.json name, Gradle root project name, display names), jest.config.js and jest.setup.js, a replacement tests/App.test.tsx, .eslintrc.js ignore patterns, .prettierignore, and a root LICENSE only when app-directory is ..

  • .maestro/smoke.yaml smoke-test skeleton (both toolchains).

  • UI contract files (both toolchains): .ui-quality.yml, src/dev/ui-gallery/** (gallery host, registry and test), .maestro/config.yaml, .maestro/subflows/ui-capture.yaml and .maestro/ui/App.yaml; eslint-plugin-react-native-a11y added as a devDependency; for a new scaffold the gallery-wrapping entry file and the deep-link scheme (expo.scheme for Expo; Android intent-filter and iOS CFBundleURLTypes for Metro). Screenshots land in .ui-screenshots/ when the Maestro capture flow runs.

  • sonar-project.properties (only when sonar is not none).

  • Metro only: the outcome of each native verification — ./gradlew assembleDebug, bundle install, bundle exec pod install and the simulator xcodebuild — each passed, failed or unverified locally.

  • A report that opens by repeating the resolved toolchain and the announced mode (from scratch or updating an existing setup), the accessibility level and .ui-quality.yml action, whether the UI gallery is wired into the entry, the Maestro capture flow’s status (unverified without a simulator, emulator or debug build), lists per tooling file the action taken (created / updated / replaced / kept as is / removed) and each mismatch a kept artifact leaves with this run’s choices, the visibility used for Sonar and where it came from, unresolved <placeholder> values still needing manual fill-in (e.g. app.json’s bundle identifiers, the Maestro `<app-id>), and required warnings before building, committing, or submitting to a store.

Execution flow

flowchart TD A[Start /iru-setup-react-native-app] --> B["Step 0: parse args\n(toolchain, app-id, ...)"] B --> E["Step 1: Resolve app-directory\n(root if empty, else confirmed subdirectory)"] E --> F{"package.json exists\nat app-directory?"} F -- no --> G["Announce: scaffolding from scratch"] F -- "yes, no react-native dependency" --> H["Warn: not a React Native project;\nask different directory or stop\n(regardless of on-existing)"] H -- stop --> K[Report app-directory untouched; end] H -- different directory --> E F -- "yes, React Native app" --> T0["Detect toolchain: expo dependency = expo;\nmetro.config.js / android+ios / CLI = metro"] T0 --> T1{"toolchain arg\ndisagrees?"} T1 -- yes --> T2["Ask: use detected toolchain or stop"] T2 -- stop --> K T2 -- detected --> I T1 -- no --> I["Announce: updating existing app;\nlist tooling files found; scaffolder never re-run"] I --> J{"on-existing keep/update/replace?"} J -- yes --> L[Apply it to every existing file] J -- no --> M["AskUserQuestion per file:\nUpdate / Keep as is / Replace / Stop"] M -- Stop --> K M -- other --> N L --> N G --> N["Step 2: Ask toolchain (if unresolved), then app-name,\napp-slug, app-id, license, developer info, open-source,\nvisibility, sonar, native-builds (expo only), distribution,\naccessibility (stand-alone)"] N --> O["Step 3: Infer owner/repo/host\nfrom git remote"] O --> P["Step 4: Resolve devDependency versions\n(npm view, or the fallback table)"] P --> P2{"toolchain?"} P2 -- expo --> P3["Step 5: Read reference/expo-templates.md"] P2 -- metro --> P4["Step 5: Read reference/metro-templates.md"] P3 --> Q{"Scaffolding from scratch?"} P4 --> Q Q -- yes, expo --> R1["Step 6: create-expo-app;\nset LICENSE, app.json name/slug/app-id"] Q -- yes, metro --> R2["Step 6: react-native-community cli init;\napply identity fix-ups"] Q -- no --> S R1 --> S["Step 6: Merge package.json/tsconfig fragments;\nwrite per-toolchain tooling files\nper the per-file decision"] R2 --> S S --> U{"toolchain?"} U -- expo --> V1["Step 7: npm install; add devDependencies\n(incl. a11y lint plugin); npx expo lint; expo install web deps;\ntest, lint, typecheck, web export"] U -- metro --> V2["Step 7: npm install; JS checks;\ngradlew assembleDebug; on macOS bundle install,\npod install, simulator xcodebuild\n(unverified if tooling missing)"] V1 --> W["Step 8: Report toolchain, per-file actions,\nmismatches, version fallbacks, native verification, warnings"] V2 --> W

Dependencies

Invokes

None — iru-setup-react-native-app is a leaf skill in the TypeScript/Node bootstrap pipeline; it does not call any other skill in this catalog. Its Step 8 report mentions Check License, Setup TypeScript Gitignore, and Setup TypeScript GitHub Workflows as follow-up work for the user, but never invokes them itself.

Invoked by

iru-setup-react-native-app itself does not spawn any Agent — it only runs shell commands (create-expo-app, @react-native-community/cli, npm, npx expo, and for metro ./gradlew, Bundler/CocoaPods and xcodebuild) and edits files in app-directory.

Source

SKILL.md on GitHub — the file this page was generated from.