microsoft/rnw-integration
Automate React Native Windows integration with upstream React Native nightly versions. Use when: upgrading RNW to a newer React Native nightly, finding target nightly versions, preparing integration PRs, updating package dependencies.
npx skills add https://github.com/microsoft/react-native-windows --skill rnw-integration
Automates the process of integrating React Native Windows with upstream React Native nightly builds.
Extract the current react-native version from vnext/package.json:
$packageJson = Get-Content -Path "vnext/package.json" | ConvertFrom-Json
$currentVersion = $packageJson.devDependencies.'react-native'
Write-Host "Current version: $currentVersion"
Expected format: 0.XX.0-nightly-YYYYMMDD-<commit-hash>
After completing this step, confirm: Step 1 completed successfully.
Extract the date from the nightly version string:
# Parse version like: 0.84.0-nightly-20260107-58bc6c3e3
$versionMatch = $currentVersion -match 'nightly-(\d{8})'
$currentDate = [DateTime]::ParseExact($matches[1], 'yyyyMMdd', $null)
Write-Host "Current nightly date: $($currentDate.ToString('yyyy-MM-dd'))"
After completing this step, confirm: Step 2 completed successfully.
$targetDate = $currentDate.AddDays(7)
$targetDateStr = $targetDate.ToString('yyyyMMdd')
Write-Host "Target date (+7 days): $($targetDate.ToString('yyyy-MM-dd'))"
After completing this step, confirm: Step 3 completed successfully.
Query npm for available nightly versions around the target date:
# Get all nightly versions for the target month
$yearMonth = $targetDate.ToString('yyyyMM')
$nightlyVersions = npm view react-native versions --json 2>$null |
ConvertFrom-Json |
Where-Object { $_ -like "*nightly-$yearMonth*" }
# Find exact match or next available
$targetVersion = $nightlyVersions | Where-Object { $_ -like "*nightly-$targetDateStr*" } | Select-Object -First 1
if (-not $targetVersion) {
Write-Host "No nightly for $targetDateStr, finding next available..."
$targetVersion = $nightlyVersions | Where-Object {
$_ -match 'nightly-(\d{8})' -and [int]$matches[1] -gt [int]$targetDateStr
} | Select-Object -First 1
}
Write-Host "Target nightly version: $targetVersion"
After completing this step, confirm: Step 4 completed successfully.
Write-Host ""
Write-Host "=== Integration Summary ==="
Write-Host "Current Version: $currentVersion"
Write-Host "Current Date: $($currentDate.ToString('yyyy-MM-dd'))"
Write-Host "Target Date: $($targetDate.ToString('yyyy-MM-dd'))"
Write-Host "Target Version: $targetVersion"
After completing this step, confirm: Step 5 completed successfully.
Run the integration script with the target nightly version to update all dependencies:
yarn integrate-rn $targetVersion
This command updates all react-native and @react-native/* package references across the monorepo to the target nightly version.
After completing this step, confirm: Step 6 completed successfully.
After running the integration script, search all package.json files for any remaining references to the previous nightly version and update them:
# Find all package.json files containing the previous nightly version
$packageFiles = Get-ChildItem -Path . -Filter "package.json" -Recurse -File |
Where-Object { $_.FullName -notlike "*node_modules*" }
$updatedFiles = @()
foreach ($file in $packageFiles) {
$content = Get-Content -Path $file.FullName -Raw
if ($content -like "*$currentVersion*") {
Write-Host "Found previous version in: $($file.FullName)"
$newContent = $content -replace [regex]::Escape($currentVersion), $targetVersion
Set-Content -Path $file.FullName -Value $newContent -NoNewline
$updatedFiles += $file.FullName
}
}
Write-Host ""
Write-Host "=== Updated $($updatedFiles.Count) package.json files ==="
$updatedFiles | ForEach-Object { Write-Host " - $_" }
This ensures all references to the previous nightly version are updated, including:
dependenciesdevDependenciespeerDependenciesresolutionsAfter completing this step, confirm: Step 7 completed successfully.
After updating all package.json files, remove all node_modules directories across the repo and perform a fresh install to ensure the dependency tree is consistent with the new versions:
# Delete all node_modules directories (excluding .git)
Get-ChildItem -Path . -Directory -Filter "node_modules" -Recurse |
Where-Object { $_.FullName -notlike "*.git*" } |
ForEach-Object {
Write-Host "Removing: $($_.FullName)"
Remove-Item -Recurse -Force $_.FullName
}
Write-Host "All node_modules directories removed."
Then run a fresh install at the root of the repo:
yarn install
If yarn install fails (e.g., due to peer dependency mismatches or build script errors), diagnose and fix the issue:
yarn install.package.json still points to an old version, update it to the target nightly version.After completing this step, confirm: Step 8 completed successfully.
Run the override upgrade tool to resolve conflicts in platform-specific override files:
npx react-native-platform-override upgrade
This interactive tool helps resolve conflicts between upstream React Native changes and RNW platform overrides. It will:
After completing the upgrade, verify the overrides are valid:
yarn validate-overrides
After completing this step, confirm: Step 9 completed successfully.
After completing all integration steps, commit the changes:
git add -A
git commit -m "Integrate RN $targetVersion"
This creates a single commit with all the integration changes including:
After completing this step, confirm: Step 10 completed successfully.
Run yarn validate-overrides to identify which folders need to be replaced with upstream versions, then download and replace them:
# Extract commit hash from target version (last 8 characters after final hyphen)
# Example: 0.85.0-nightly-20260114-f15985f4f -> f15985f4f
$commitHash = ($targetVersion -split '-')[-1]
Write-Host "Commit hash: $commitHash"
# Run validate-overrides and capture failing paths
$validateOutput = yarn validate-overrides 2>&1
$failingPaths = $validateOutput | Where-Object { $_ -match '^ - ' } | ForEach-Object { $_.Trim(' -') }
if ($failingPaths.Count -eq 0) {
Write-Host "No failing overrides to update"
return
}
Write-Host "Found $($failingPaths.Count) failing override paths to update"
# Create temp directory (outside git tracking)
$tempDir = "$env:TEMP\rn-upstream-$commitHash"
if (Test-Path $tempDir) { Remove-Item -Recurse -Force $tempDir }
New-Item -ItemType Directory -Path $tempDir | Out-Null
# Download react-native repo at the specific commit
Write-Host "Downloading react-native at commit $commitHash..."
git clone --depth 1 --filter=blob:none --sparse https://github.com/facebook/react-native.git $tempDir
Push-Location $tempDir
git sparse-checkout set packages/rn-tester
git fetch --unshallow
git checkout $commitHash
Pop-Location
# Replace each failing path with upstream version
foreach ($path in $failingPaths) {
# Convert absolute path to relative
$relativePath = $path -replace [regex]::Escape((Get-Location).Path + '\'), ''
# Map RNW paths to upstream rn-tester paths
$upstreamPath = $null
if ($relativePath -like "packages\@react-native\tester\*") {
# packages\@react-native\tester\X -> packages\rn-tester\X
$subPath = $relativePath -replace '^packages\\@react-native\\tester\\', ''
$upstreamPath = "$tempDir\packages\rn-tester\$subPath"
}
elseif ($relativePath -like "vnext\ReactCopies\*") {
# vnext\ReactCopies\X -> packages\rn-tester\X
$subPath = $relativePath -replace '^vnext\\ReactCopies\\', ''
$upstreamPath = "$tempDir\packages\rn-tester\$subPath"
}
if ($upstreamPath -and (Test-Path $upstreamPath)) {
Write-Host "Replacing $relativePath with upstream version..."
Remove-Item -Recurse -Force $relativePath
Copy-Item -Recurse -Force $upstreamPath $relativePath
} else {
Write-Host "WARNING: Could not find upstream path for $relativePath"
}
}
# Cleanup temp directory
Remove-Item -Recurse -Force $tempDir
Write-Host "Overrides updated from upstream commit $commitHash"
Verify the overrides are valid after the update:
yarn validate-overrides
After completing this step, confirm: Step 11 completed successfully.
After Steps 9-11, some override files may still contain merge conflict markers (<<<<<<<, =======, >>>>>>>). This step resolves them safely with human approval for each hunk.
NON-NEGOTIABLE CONSTRAINTS:
<<<<<<< ... ======= ... >>>>>>> region is one hunk)>>>>>>> markers are intentional (e.g., E2E react-native-platform-override files) — do not "fix" thoseflowconfig.windows.conflict untouchedBefore resolving any conflicts, mine historical "good" RNW-fork commits for conflict-resolution patterns. Create or update references/conflict-patterns.md.
Reference commits to study:
ceeaddc0d19161e4fa0e01139a721b758d0269ce
9f26c15efab7b51dd8544f50d2a26905bb875223
1d7f44c4ad58a3ffa506cdc618c92d0dcebaf238
e5d505e43d47a7cc603cd1be3d875dc0e3ab6ab9
3e3b133fcb32f39f485b075858eaa7ff55d0d912
9b40dac70b27ec2e5c3d0449bca50c841764e413
d73784caa0d7e91a6c20fa485cf9ccb7923a1d40
b6315a67451f9a2526ba1a5f3814d4ca6564342c
bb50808b9f7e63da8af5511bf4b2faf064add19f
For each commit:
Scan the repository for conflict markers:
# Find all files with conflict markers (excluding node_modules, intentional files)
$conflictFiles = Get-ChildItem -Path . -Recurse -File |
Where-Object { $_.FullName -notlike "*node_modules*" -and $_.Name -ne "flowconfig.windows.conflict" } |
ForEach-Object {
$content = Get-Content -Path $_.FullName -Raw -ErrorAction SilentlyContinue
if ($content -match '<<<<<<<') { $_.FullName }
}
Write-Host "=== Files with conflict markers ==="
$conflictFiles | ForEach-Object { Write-Host " - $_" }
Write-Host "Total: $($conflictFiles.Count) files"
Split each conflicted file into individual hunks for review.
IMPORTANT: Guard against "previously-intentionally-skipped" upstream changes.
In some prior RNW integrations, certain upstream changes were intentionally NOT taken for specific files (e.g., because RNW has a divergent Windows copy, platform overrides, or intentional conflict markers). In later integrations, those same upstream changes may reappear and must NOT be automatically accepted.
Before resolving any conflict hunk, you MUST first inspect the actual upstream change using the compare URL:
https://github.com/facebook/react-native/compare/<previous-commit>...<target-commit>$currentVersion)$targetVersion)https://github.com/facebook/react-native/compare/58bc6c3e3...f15985f4fRequired steps for each hunk:
// [Windows) or is substantially different → upstream changes may be intentionally NOT applicableLow/Medium/High)Low or Medium as appropriate and request human approvalOutput requirement for each hunk proposal:
Low/Medium/High)CRITICAL: Separate upstream changes — do NOT bundle unrelated changes into one resolution.
When the THEIRS (upstream) side contains MULTIPLE independent changes (e.g., a syntax rename $ReadOnly → Readonly AND a semantic change like adding Omit<>), treat each change independently:
Omit<>, added/removed parameters) from THEIRS unless they are explicitly part of the same conflict region AND you have confirmed via the compare URL that the semantic change is safe for RNWExample of what NOT to do:
THEIRS: Readonly<{ ...Omit<ViewProps, 'experimental_accessibilityOrder'>,
OURS: $ReadOnly<{ ...ViewProps,
WRONG resolution: Readonly<{ ...Omit<ViewProps, 'experimental_accessibilityOrder'>, ← silently adopted Omit from upstream
CORRECT resolution: Readonly<{ ...ViewProps, ← only took the syntax change, kept OURS content
CRITICAL: Line-by-line diff the OURS and THEIRS blocks before proposing.
Before proposing any resolution, explicitly diff the OURS and THEIRS blocks line by line:
$ReadOnly → Readonly)$ReadOnly → Readonly, $FlowFixMe[incompatible-return] → $FlowFixMe[incompatible-type]) — safe to take from THEIRSOmit<> additions, new parameters) — do NOT take unless confirmed safe// [Windows blocks) — ALWAYS keeptype Props = { without $ReadOnly) and THEIRS adds one (Readonly<{), keep OURS as-is — do NOT add wrappers that OURS didn't have{||}, {|) and THEIRS uses inexact ({}, {), keep OURS exact syntaxFor each hunk, generate a PROPOSAL:
Low / Medium / HighAfter generating the batch summary table, present EACH hunk individually for review. For every hunk show:
======= to >>>>>>> content (RNW Override side)<<<<<<< to ======= content (Upstream side)<<<<<<< and after >>>>>>>references/conflict-patterns.md applies (P1-P7)<<<<<<< through >>>>>>> inclusive)Low / Medium / High10. Approval prompt: "Approve applying this hunk? (Yes/No)"
Wait for the user's response before proceeding to the next hunk. If the user says:
File-type specific rules:
.js/.ts/.tsx: Preserve // [Windows sections; prefer upstream for pure formatting changes.cpp/.h: Check for #ifdef WINDOWS / platform-specific guards; upstream-first for shared code.flowconfig/config files: Keep RNW-specific entries, add new upstream entriespackage.json: Use target nightly version for all RN-related depsFolder-specific bias:
vnext/src-win/: Bias toward RNW (OURS) — these are Windows-specific overridespackages/@office-iss/: Bias toward RNW (OURS) — Win32-specificpackages/@react-native/tester/: Bias toward upstream (THEIRS) — should match upstreamvnext/ReactCopies/: Bias toward upstream (THEIRS) — should be exact copiesAfter explicit approval for a hunk:
flowconfig.windows.conflict conflictAfter all hunks are resolved:
# Verify no unintentional conflict markers remain
$remaining = Get-ChildItem -Path . -Recurse -File |
Where-Object { $_.FullName -notlike "*node_modules*" -and $_.Name -ne "flowconfig.windows.conflict" } |
ForEach-Object {
$content = Get-Content -Path $_.FullName -Raw -ErrorAction SilentlyContinue
if ($content -match '<<<<<<<') { $_.FullName }
}
if ($remaining.Count -eq 0) {
Write-Host "All conflicts resolved!"
} else {
Write-Host "WARNING: $($remaining.Count) files still have conflict markers"
$remaining | ForEach-Object { Write-Host " - $_" }
}
# Commit resolved conflicts
git add -A
git commit -m "Resolve merge conflicts for RN $targetVersion integration"
After completing this step, confirm: Step 12 completed successfully.
After resolving merge conflicts, perform an initial functional smoke check by building and running RNW Playground locally.
First, ensure the yarn lockfile is up to date (upstream dependency changes during integration may have introduced new packages):
yarn install
If the lockfile changed, commit it before building:
git add yarn.lock
git diff --cached --quiet yarn.lock || git commit -m "Update yarn.lock for RN $targetVersion integration"
Then build the Playground:
# Find MSBuild path
$msbuild = & "${env:ProgramFiles(x86)}\Microsoft Visual Studio\Installer\vswhere.exe" -latest -requires Microsoft.Component.MSBuild -find MSBuild\**\Bin\MSBuild.exe
Write-Host "MSBuild: $msbuild"
# Build the Playground composition via the solution (required for SolutionPath property)
# Target only the playground-composition.Package project
& $msbuild packages/playground/windows/playground-composition.sln /t:playground-composition_Package /p:Configuration=Debug /p:Platform=x64 /p:RestoreLockedMode=false /m
# If build succeeds, launch the app to verify runtime behavior
The solution file is at: packages/playground/windows/playground-composition.sln
The target project is: playground-composition.Package (built via /t:playground-composition_Package)
Some integration failures are caused by upstream changes that were previously fixed during earlier RNW integrations via patches applied in the RNW forked repository. The following commits contain known previous integration build/runtime fixes:
885aa5b51f1b4493a4bed16fce846270adb44d70
01bfeefcb58a0688713c42b5c5b867319a25029e
76a133e7ff81e148db6a9cd1d7d4c7a7aca86cb6
477ea4915576235f82e2b6349917f345ce929180
21285f1247cec3bb9667859c5698d10930eefd1b
9a2e17697526ed219a33d96f7acf0903669f7585
aabe43e85e3e201ab99e0ac71ef6ee7dcf442110
5a6fdb82771e9cbc6ee019dab5766f03761df6d0
48e7a08931a59be27cc8709a511b7c6aea3d6900
61392d901dd036b3abe456de1f5ace8571954811
9fb535e0285883ba95b39018d26ff2ba7b876d53
cf1e9188c391c0411205657bbdcd8feb5d851fc4
For EACH commit listed above:
references/conflict-patterns.mdPresent:
Low / Medium / High)Ask explicitly: "Approve applying this fix? (Yes/No)"
If confidence is Low:
Maintain a recovery log for each evaluated commit:
| Commit | Issue Matched | Confidence | Approval | Applied/Skipped |
|--------|--------------|------------|----------|-----------------|
| 885aa5b... | (description) | High/Med/Low | Yes/No | Applied/Skipped |
| ... | ... | ... | ... | ... |
After all fixes are applied and Playground builds successfully:
git add -A
git commit -m "Apply build fixes for RN $targetVersion integration"
After completing this step, confirm: Step 13 completed successfully.
After the build succeeds, deploy and launch the Playground app to verify runtime behavior.
IMPORTANT: Debug builds are NOT signed. Do NOT attempt to install via MSIX (Add-AppxPackage -Path *.msix) — it will fail with 0x800B0100: No signature was present. Instead, use loose layout registration via Add-AppxPackage -Register <AppxManifest.xml>, which does not require signing or certificates.
# Register the app from the loose layout (no signing needed for debug builds)
$appxManifest = "packages\playground\windows\playground-composition.Package\bin\x64\Debug\AppX\AppxManifest.xml"
if (Test-Path $appxManifest) {
Write-Host "Registering app from: $appxManifest"
Add-AppxPackage -Register $appxManifest -ForceApplicationShutdown
} else {
# Fallback: search for AppxManifest.xml in build output
$appxManifest = Get-ChildItem -Path "packages\playground\windows" -Filter "AppxManifest.xml" -Recurse -ErrorAction SilentlyContinue |
Where-Object { $_.FullName -like "*bin*Debug*AppX*" } |
Select-Object -First 1 -ExpandProperty FullName
if ($appxManifest) {
Write-Host "Registering app from: $appxManifest"
Add-AppxPackage -Register $appxManifest -ForceApplicationShutdown
} else {
Write-Host "ERROR: No AppxManifest.xml found. Build may have failed."
}
}
# Launch the app
$appPackage = Get-AppxPackage -Name "*playground*" | Select-Object -First 1
if ($appPackage) {
$appId = (Get-AppxPackageManifest $appPackage).Package.Applications.Application.Id
$fullName = $appPackage.PackageFamilyName
Write-Host "Launching: $fullName!$appId"
Start-Process "shell:AppsFolder\$fullName!$appId"
Write-Host "Playground launched successfully. Verify the app renders correctly."
} else {
Write-Host "WARNING: Could not find installed Playground package. Try deploying from Visual Studio."
}
After verifying the app launches and renders correctly:
After completing this step, confirm: Step 14 completed successfully.
When integrating a new nightly version, these files typically need updates:
| File | Dependencies to Update |
|------|----------------------|
| vnext/package.json | react-native, @react-native/* packages |
| package.json (root) | react-native in resolutions |
| Various app package.json files | Peer dependencies |
Take microsoft/rnw-integration from the repository into ~/.claude/skills for personal
use, or into .claude/skills inside a project.
The agent identifies a skill by the name field in its header. Two skills with the
same name cannot sit side by side — one of them will be ignored.
The instructions reference npx.
Without those the skill loads but fails at the first command.