React Native New Architecture Migration Skill
⛔ ABSOLUTE RULE — READ FIRST:
NEVER write a specific date (month, year, or day) for any SDK/library EOL, deprecation, or sunset UNLESS you have verified it via
web_searchand can cite the exact official source URL in the same sentence.Self-check before writing any vendor SDK note: Ask yourself: "Did I verify this date via
web_searchand do I have the official source URL?" If no — DELETE the date and write "status unverified" instead.What to write:
- If
web_searchconfirmed a specific EOL date:"Deprecated per [exact URL] — EOL [date]. Vendor recommends [replacement]."- If
web_searchfound a deprecation notice but no specific date:"Deprecated per [exact URL]. Vendor recommends [replacement]."- If
web_searchfound NO deprecation notice:"Maintenance status unverified — check [vendor docs URL]."- If you did NOT run
web_searchfor this package:"SDK status unverified."The rule is: date without source URL = BANNED. A date is only valid when the source URL appears in the same sentence. This rule exists because LLMs hallucinate dates with high confidence — the URL is what makes the claim trustworthy. This applies to EVERY table cell, action plan step, effort estimate, and executive summary sentence. No exceptions.
This skill audits a bare React Native project (or monorepo) and produces a date-stamped MIGRATION_AUDIT_YYYYMMDD.md. A styled PDF (MIGRATION_AUDIT_YYYYMMDD.pdf) is generated on request after the Markdown is delivered. It does NOT rewrite code — it identifies what needs to change, classifies severity, and gives the developer a clear, prioritized plan.
What this skill covers
- Dependency audit: classify npm packages by New Arch compatibility (with live lookup for unknowns)
- JS/TS source scan: detect Bridge-era patterns (NativeModules, requireNativeComponent, EventEmitter misuse)
- iOS native audit: scan for RCTBridgeModule, RCT_EXPORT_MODULE, RCT_EXPORT_METHOD patterns
- Android native audit: scan for ReactContextBaseJavaModule, @ReactMethod, @ReactProp, ReactPackage patterns
- Interop vs rewrite triage: distinguish true blockers from interop-compatible items (most old native modules work under interop)
- Two-tier effort estimate: separate "effort to enable New Arch" (true blockers only) from "effort for full modernization" (TurboModule rewrites, library replacements)
- Monorepo support: detect and scan each workspace package independently
- Delta mode: diff new findings against a previous audit to show progress
- Token tracking: record approximate tokens consumed by the audit session
- PDF output: generate a styled PDF on request after the Markdown report is delivered
Step 0 — Project fingerprinting & monorepo detection
0a — Detect monorepo
cat package.json | python3 -c "import sys,json; d=json.load(sys.stdin); print(d.get('workspaces','none'))"
ls packages/ apps/ libs/ 2>/dev/null
If monorepo detected: Collect each workspace package path. For each sub-package containing react-native in its dependencies or a ios/ or android/ directory, add it to the scan list. Run Steps 1–7 once per package. Produce one combined report with per-package sections (see monorepo structure at end of this file). Label each section clearly.
If single app: proceed normally.
0b — Fingerprint
Read per package — treat every file as optional and handle gracefully if missing:
# Safe reads — never fail if file absent
[ -f package.json ] && cat package.json || echo "{}"
[ -f app.json ] && cat app.json || echo "{}"
[ -f android/build.gradle ] && cat android/build.gradle || echo ""
[ -f android/gradle.properties ] && cat android/gradle.properties || echo ""
[ -f ios/Podfile ] && cat ios/Podfile || echo ""
[ -f babel.config.js ] && cat babel.config.js || echo ""
Extract and handle each missing case explicitly:
- RN version — read from
react-nativeinpackage.jsondependencies. If absent, note "RN version not detected" and still run the full audit. - Hermes enabled — check
package.jsonandroid/ios sections and Podfile. If neither present, note "Hermes: Not detected". - TypeScript or Flow — check for
tsconfig.jsonor.flowconfig. If neither, note "Type system: Not detected". - New Arch already enabled — check
android/gradle.propertiesfornewArchEnabled=trueandios/PodfileforRCT_NEW_ARCH_ENABLED=1. Default to "Not enabled" if files are absent. - iOS layer — if
ios/directory does not exist, skip Step 3 entirely and note "No iOS directory found — iOS audit skipped" in the report. - Android layer — if
android/directory does not exist, skip Step 4 entirely and note "No Android directory found — Android audit skipped" in the report. - JS-only library — if neither
ios/norandroid/exists butpackage.jsonis present, this may be a JS-only package in a monorepo. Run Steps 1–2 only and note the scope in the report. - Malformed package.json — if
package.jsoncannot be parsed as valid JSON, abort with a clear message: "Cannot parse package.json — please fix JSON syntax errors before running the audit."
If RN version < 0.68, prepend a prerequisite block to the report. Still run the full audit.
0c — Delta mode detection
ls MIGRATION_AUDIT_*.md 2>/dev/null | sort | tail -1
If a previous audit exists, parse its dependency table and source/native finding tables. Store them as the baseline. Record the previous audit date and effort label for comparison in Step 7.
Step 1 — Dependency audit
Run the library checker script first — do not attempt manual lookups.
python3 $(find .claude/skills ~/.claude/skills -name check_libs.py 2>/dev/null | head -1) \
--project-root .
This script:
- Checks all deps in parallel (10 workers) against reactnative.directory (live, per-package) + npm registry
- Silently skips pure-JS packages, RN core, and build tooling (no native code, irrelevant to New Arch)
- No manual overrides — every native library is fetched live, results always reflect current directory data
- Outputs
lib_report.jsonin the project root
Read the JSON report (costs ~150–200 tokens regardless of project size):
cat lib_report.json
The JSON shape is:
{
"summary": { "compatible": 20, "interop": 5, "unknown": 3 },
"pure_js_skipped": 6,
"libraries": [
{
"package": "react-native-snap-carousel",
"version": "^3.9.1",
"status": "interop",
"notes": "Unmaintained — no New Arch support and unlikely to receive updates. Works under interop layer (RN 0.73+) but consider replacing with an actively maintained alternative.",
"replacement": ""
}
]
}
Use this JSON directly to write the dependency audit tables in the report. Do not re-fetch, re-reason, or re-classify anything the script has already resolved.
Status values from the script:
| Value | Label | Meaning |
|---|---|---|
compatible | ✅ Compatible | Confirmed New Arch support |
interop | ⚠️ Interop OK | Works via interop layer — deferred, not permanent. Includes unmaintained libraries (noted in notes field). |
unknown | ❓ Unknown | Script could not determine — note the repo URL from notes field |
Important: The script does not mark any library as
blockingbecause the interop layer (RN 0.73+) enables most legacy native modules to work without modification. The script classifies based on directory/npm metadata. Actual blocking status requires deeper analysis — see "When to classify a library as ❌ Blocking" below.
Resolving ❓ Unknown packages via web search:
For any packages the script marks as unknown, perform a web search for each one:
"{package-name}" react native new archit