Skip to main content

08 — Engineering Best Practices for a React + React Native Library Monorepo (2025–2026)

Research date: 2026-08-15. Scope: how to build, test, document, secure and publish the Rasd Forms open-core library (React web + React Native + shared core) as a pnpm/Turborepo monorepo. Facts below were verified against primary sources (release blogs, official docs, GitHub issues, pricing pages) unless flagged "unverified" or "conflict".

Summary

  1. Platform baseline is now firmly "New Architecture + React 19 + ESM": React Native 0.82 (Oct 2025) runs only on the New Architecture; 0.85 (Apr 2026) requires Node ≥20.19.4 and moves the Jest preset to @react-native/jest-preset; 0.87 (Aug 11 2026) makes the Strict TypeScript API default, removes deep imports (react-native/Libraries/*), requires Node ≥22.13 and ships Metro 0.87 [3][5][6]. Expo SDK 56 (May 21 2026) = RN 0.85 + React 19.2; SDK 54 was the last SDK with Legacy Architecture [3][7].
  2. Ship ESM-only from Metro-friendly, extension-less .js output. react-native-builder-bob's default template dropped dual CJS/ESM in 0.40.0 (Apr 2025); Metro honours exports by default since Metro 0.82 / RN 0.79; Node 20.19+/22.12+ can require() ESM. Dual publishing now mostly creates the "dual package hazard" (two copies of React context/state) [1][2][11][39].
  3. exports map with react-native condition first, types first-within-branch, default last, plus a private "source" condition for the monorepo. When a subpath matches exports, Metro no longer expands .native.js/.ios.js extensions, so platform splitting must be done by separate packages or by pointing react-native condition targets at native entry files [1][11][38].
  4. tsup is dead; tsdown (Rolldown) is its named successor; rslib (Rspack) is a strong 0.x alternative; keep bob for RN packages (Babel-based, Hermes-safe, codegen-aware) [9][10][30].
  5. TypeScript 6.0 (Mar 23 2026) flipped defaults (strict, module: esnext, target: es2025, types: []) and deprecates baseUrl, moduleResolution: node, target: es5; TS 7 (Go port) is in preview. Adopt isolatedDeclarations now so tsdown/rslib/oxc can emit .d.ts without the type-checker; TypeDoc will feature-freeze once TS 7 ships and API Extractor has historically choked on isolatedDeclarations [8][33][34].
  6. Publish with Changesets + npm Trusted Publishing (OIDC, GA July 31 2025): no long-lived tokens, npm ≥11.5.1, Node ≥22.14, id-token: write, one trusted publisher per package, provenance auto for public repos. Beware pnpm 11.0.8 broke OIDC publish (fix PR #11526) — pin pnpm or publish via npm publish/changeset publish [12][13][14][26].
  7. Supply chain is the #1 2025–26 risk: Shai-Hulud worms (Sept and Nov 2025; 500–796 packages incl. chalk/debug) and the Nx "s1ngularity" token theft (Aug 2025). pnpm 11 (Apr 2026) defaults minimumReleaseAge=1440 min, blockExoticSubdeps=true, strictDepBuilds, allowBuilds allow-list; pair with Socket/GHSA audit, SHA-pinned Actions and Node 24 runners (default from June 16 2026) [24][25][27][36].
  8. Testing stack: Vitest 4 (Oct 22 2025; browser mode stable, toMatchScreenshot), RTL for web, RNTL v14 (Jun 5 2026; React ≥19, RN ≥0.78, async render), Storybook 10 (ESM-only; @storybook/react-native v10 + @storybook/react-native-web-vite), Playwright for web E2E, Maestro for RN E2E (Expo's documented EAS Workflows tool; Detox 20 covers RN 0.77–0.84 but treats Expo as community-only) [15][16][17][18][19][20].
  9. Lint/format: ESLint 9 flat config + typescript-eslint v8 (projectService) remains necessary for eslint-plugin-react-hooks compiler rules and jsx-a11y; Biome 2.5 (Jun 2026, 500+ rules, plugin code-fixes) is a fast formatter/first-pass linter but not a full replacement yet [23].
  10. Docs: Docusaurus 3.10 (Apr 2026, last 3.x, future.v4 flag) is the safe multi-locale/RTL choice; Fumadocs if docs live in a Next.js app; Mintlify Starter is $0 (5 editors, custom domain) with Pro "custom" pricing per the live pricing page — third-party blogs quote historic $250–300/mo Pro (conflict) [21][37][41].

1. Platform baseline (what Rasd Forms must target)

LayerCurrent (Aug 2026)Notes for a library
React19.2 (Oct 2025), patch 19.2.8 (Jul 21 2026) [40]Activity, useEffectEvent, owner stacks. No React 20 announced.
React Native0.87 (Aug 11 2026); 0.85 (Apr 7 2026); 0.83 (Dec 2025, React 19.2); 0.82 (Oct 8 2025, New Arch only) [3][4][5][6]0.87: Strict TS API default, deep imports removed (opt-out via customConditions: ["react-native","react-native-legacy-deep-imports"] until 0.88), Node ≥22.13, Metro 0.87, AGP 9, experimental SwiftPM. 0.85: Node ≥20.19.4, @react-native/jest-preset, StyleSheet.absoluteFillObject removed. 0.82: uncaught promise rejections now console.error (relevant to sync engines).
ExpoSDK 56 stable May 21 2026 (RN 0.85, React 19.2, TS 6 support, import.meta on, expo-sqlite ArrayBuffer/blob + session changesets); SDK 55 (Feb 2026, RN 0.83); SDK 54 (RN 0.81) last Legacy Arch [7]SDK 57 announced as minor-like, fixes Hermes V1 memory regression.
HermesV1 default from RN 0.84 (Feb 2026) [4]Avoid import.meta and top-level await in shipped code; bob docs: import.meta is a syntax error in bare Metro [1].
TypeScript6.0 (Mar 23 2026); 7.0 native preview "extremely close" [8]New defaults strict, module: esnext, target: es2025, types: [], noUncheckedSideEffectImports. Deprecated: baseUrl, moduleResolution node/classic, target es5, outFile, esModuleInterop:false.
NodeNode 22 LTS baseline: pnpm 11 ≥22, RN 0.87 ≥22.13, npm OIDC ≥22.14, RNTL 14 ^22.13 ‖ ≥24 [13][18][24]Set "engines": {"node": ">=22.13"} for tooling; runtime library code has no Node requirement.

Implication: a library launching in late 2026 can reasonably declare react ^19, react-native >=0.81 (0.79 = exports-aware Metro; 0.81/SDK 54 = last legacy) and Expo SDK ≥54, and test against 0.85/SDK 56 and 0.87.

2. Monorepo: pnpm 11 + Turborepo 2.x (vs Nx 23)

  • pnpm 11.0 (Apr 28 2026): Node ≥22; security defaults (minimumReleaseAge: 1440, blockExoticSubdeps: true, strictDepBuilds: true, verifyDepsBeforeRun: install); allowBuilds map replaces onlyBuiltDependencies/neverBuiltDependencies; pnpm settings move from .npmrc to pnpm-workspace.yaml; native publish/dist-tag/deprecate (npm CLI fallback removed); new pnpm ci, pnpm sbom (CycloneDX/SPDX), pnpm peers check; store v11 uses SQLite; catalog: protocol for single-source versions [24][25].
  • Turborepo: 2.6 (Oct 2025), 2.7 (Dec 2025: Biome rule, Yarn catalogs, composable config), 2.8 (Jan 2026: git worktrees, agent skill, task descriptions), 2.9 (Mar 2026: up to 96% faster, deprecations for 3.0), 2.10 (Jun 24 2026: graceful shutdown, deferred input hashing, composable --affected/--filter) [31].
  • Nx: 22 (Oct 2025) recognises pnpm catalogs; 23 (Jun 2026) requires Node 22 [32][43]. Nx is heavier (plugins, inferred tasks, Nx Cloud) and was the victim of the Aug 2025 "s1ngularity" token-theft incident, which is a reminder that the build tool's own supply chain matters [27].

Verdict: pnpm workspaces + Turborepo (remote cache optional) is the lowest-ceremony choice for a ~10-package library repo; Nx only if you want generators/inferred targets and Nx Agents.

3. TypeScript configuration

  • Base tsconfig for packages: "strict": true, "moduleResolution": "bundler" (bob explicitly warns against node16/nodenext because they break platform-specific resolution) [1], "module": "esnext", "target": "es2020" or higher for web (Hermes handles down-levelling via Babel in RN), "isolatedModules": true, "isolatedDeclarations": true, "verbatimModuleSyntax": true, "types": [] (TS 6 default) plus explicit "types": ["react","react-native"] where needed, "customConditions": ["react-native", "rasd-source"] for the monorepo source condition [1][8].
  • isolatedDeclarations forces explicit return types on exports; payoff is that tsdown/rslib/oxc-transform generate .d.ts in milliseconds without tsc [9][30][44]. Cost: API Extractor once errored on the flag ("Unknown compiler option 'isolatedDeclarations'", rushstack #4877, may be fixed in newer versions — verify before adopting) [34].
  • Turn on TS 6 now with "ignoreDeprecations": "6.0" only where needed; then trial @typescript/native-preview (TS 7) in CI as a non-blocking job [8].

4. Build tools

ToolEngineStatus (Aug 2026)Fit for Rasd Forms
tsupesbuildREADME: "This project is not actively maintained anymore. Please consider using tsdown instead." [10]Do not start new packages on it.
tsdownRolldown (Rust)Active; npx tsdown-migrate; defaults: ESM, auto-clean, dts auto if types field, reads engines.node; unbundle: true for per-file output; workspace mode; auto exports generation; 3–10× faster than tsup per third-party benchmarks [9]Web/core/builder/storage-web packages.
rslibRspack0.x, ByteDance; ESM/CJS/UMD, isolated declarations, CSS Modules/Lightning CSS, Module Federation [30]Alternative if you need CSS pipelines or MF; watch for 1.0.
rollup + pluginsJSMature; more config; slower dtsOnly for exotic needs.
unbuildrollup/mkdistUnJS; "stub" mode nice for monoreposFine, but tsdown covers it.
react-native-builder-bob 0.40+Babel + tscDefault template ESM-only since 0.40.0 (Apr 4 2025); targets module, commonjs, typescript, codegen; supports source condition, .native.js etc. [1][2]Native packages (@rasd-forms/native, storage-sqlite).

Bob's ESM guidance to internalise [1]: named exports only (no default exports); do not use .mjs/.cjs/.mts/.cts with Metro; platform-specific extensions break Node ESM; extension-less specifiers plus .js output; do not rely on import.meta.

5. package.json exports, conditions, dual publish, tree-shaking

Reference shape (per package):

{
"name": "@rasd-forms/react",
"type": "module",
"sideEffects": false,
"main": "./dist/index.js",
"types": "./dist/index.d.ts",
"exports": {
".": {
"rasd-source": "./src/index.ts",
"types": "./dist/index.d.ts",
"react-native": "./dist/index.native.js",
"browser": "./dist/index.js",
"default": "./dist/index.js"
},
"./styles.css": "./dist/styles.css",
"./package.json": "./package.json"
},
"files": ["dist", "src", "!**/__tests__"],
"engines": { "node": ">=20.19" }
}

Rules verified from Metro docs, publint and bob [1][11][38]:

  • Metro checks conditions in order react-native, browser, then import/require (whichever the importing file used, never both), then default; enabled by default since Metro 0.82 (RN 0.79) with unstable_enablePackageExports still available as an escape hatch. The legacy root "react-native" field is superseded by the community condition [11].
  • Once a subpath matches exports, Metro uses the exact file — no sourceExts or .ios.js/.native.js expansion; adding exports to an existing package is a breaking change unless every prior deep import is re-listed [11].
  • publint: types first, default last, module before require, dual formats need separate .d.mts/.d.cts, prefer exports/imports browser conditions over the legacy browser field, set "sideEffects": false (or an array listing CSS/polyfill files) [38].
  • ESM-only is the 2026 default for new browser/React libraries (antfu, Feb 2025; Storybook 10 itself went ESM-only). Node 20.19+/22.12+ require(esm) removes the last CJS argument for Node consumers [39][15]. If a CJS build is unavoidable (e.g., an old Jest config), keep React-context/state singletons in a single module both builds import (bob's dual-package-hazard mitigation) [1].
  • Keep src in the tarball so the source condition and source maps work for consumers debugging Hermes stack traces.

6. peerDependencies, React 19, New Architecture, React Compiler

  • Declare react, react-dom, react-native, and Expo modules (expo, expo-sqlite, react-native-mmkv/op-sqlite, react-native-svg, gesture-handler/reanimated for the builder) as peerDependencies with peerDependenciesMeta.optional = true for platform-only ones; pnpm 11 pnpm peers check and pnpm.peerDependencyRules help in the workspace [24].
  • Ranges: react ^19, react-dom ^19, react-native >=0.81 <1, expo >=54 (optional). Test matrix should include RN 0.81 (last legacy), 0.85 (SDK 56), 0.87 (Strict TS API — no react-native/Libraries/* imports allowed) [5][6].
  • React Compiler 1.0 (Oct 7 2025): the compiler must run on original source, so app pipelines cannot compile node_modules — libraries should precompile with babel-plugin-react-compiler (target: '17' | '18' | '19'), and add react-compiler-runtime as a dependency only if targeting <19; test with and without compilation [28][29]. Since Rasd Forms targets React 19, target: '19' and no runtime dependency; keep manual useMemo out of hot paths and mark opt-outs with "use no memo".

7. Versioning, releases and publishing

  • Semver + Changesets (.changeset/*.md, changeset version, changeset publish, changesets/action@v1 opening the "Version Packages" PR). Fixed-versioning group for @rasd-forms/* runtime packages so react/native/core stay in lock-step; independent for tooling packages.
  • npm Trusted Publishing (OIDC) GA July 31 2025 [12][13][14]: configure per package on npmjs.com (repo + workflow filename; only one trusted publisher per package, cloud runners only); workflow needs permissions: id-token: write; npm ≥11.5.1 and Node ≥22.14; provenance is attached automatically for public repos (Phil Nash reports needing --provenance or publishConfig.provenance: true in some cases and repository.url must exactly match git+https://github.com/org/repo.git); package must be published once before a trusted publisher can be added (community-reported). Then set the package to "Require 2FA and disallow tokens".
  • pnpm caveat: pnpm 11.0.8 broke OIDC publish (404), tracked in pnpm/pnpm#11513 with fix PR #11526; until verified, run changeset publish (which shells to npm) with npm upgraded, or pin a known-good pnpm [26].
  • Conventional Commits (commitlint + husky/lefthook) mainly to auto-generate changeset summaries; Changesets remains the source of truth for versions.
  • Add LICENSE (e.g., Apache-2.0 for the open core; separate commercial LICENSE-COMMERCIAL for pro packages) and SPDX headers // SPDX-License-Identifier: Apache-2.0 enforced by an ESLint header rule or Biome plugin; run pnpm sbom and license-checker-rseidelsohn in release CI [24].

8. API docs and docs sites

  • TypeDoc 0.28.x (typedoc-plugin-markdown for MDX output into the docs site). Risk: TypeDoc relies on TS-internal APIs; the maintainer plans a feature freeze once TS 7 ships and TS 7 support has no timeline [33]. API Extractor gives .api.md reports (great for enforcing public-API review in PRs) but had isolatedDeclarations issues [34]. Practical: TypeDoc for docs, api-extractor run --local in CI as an API-report guard, verify against your tsconfig first.
  • Docs site comparison [21][37][41]:
OptionStackProsConsCost
Docusaurus 3.10React/MDX, Rspacki18n + RTL out of the box, versioned docs, Algolia DocSearch v4/AskAI, huge ecosystem; 3.10 = last 3.x, future.v4 flagSlower dev server than Astro; opinionatedFree
FumadocsNext.jsBest if docs live in a Next.js app; OpenAPI pages; very customisableMore React/Next knowledge; i18n DIYFree
StarlightAstroFastest static output, built-in i18nNot React-native-friendly for live component demos (islands only)Free
MintlifyHostedPolished, AI assistant on paid tiersVendor lock-in; pricing page shows Starter $0 (5 editors, custom domain), Pro "custom", Enterprise; third-party posts cite historic $250–300/mo Pro (conflict — verify)$0+

For an Arabic-RTL humanitarian audience with live React demos, Docusaurus (or Fumadocs) beats Starlight/Mintlify.

9. Lint and format

  • ESLint 9 flat config (eslint.config.js at root, per-package overrides), typescript-eslint v8 with parserOptions.projectService: true (no per-package project arrays needed in monorepos), eslint-plugin-react-hooks (v6+ ships React-Compiler-powered rules), eslint-plugin-jsx-a11y, eslint-plugin-react-native-a11y, eslint-plugin-import-x, eslint-plugin-perfectionist.
  • Biome 2.5 (Jun 5 2026): 500+ rules, cross-file linting, plugin code fixes, built-in type inference for some type-aware rules [23]. Third-party comparisons put its floating-promise coverage at ~75% of typescript-eslint and note gaps vs. framework plugins (unverified numbers). Use Biome as formatter + import organiser (Turborepo 2.7 even ships a Biome rule) and keep ESLint for React/a11y/type-aware rules; do not run two formatters.

10. Testing strategy

LayerWebReact Native
Unit/componentVitest 4 (Oct 22 2025; browser mode stable via @vitest/browser-playwright, toMatchScreenshot, Playwright traces) + @testing-library/react + vitest-axe [17]Jest with @react-native/jest-preset (RN 0.85+) + RNTL v14 (Jun 5 2026: React ≥19, RN ≥0.78, Node ^22.13‖≥24; render/fireEvent are async, host-only queries; use v13 if React 18 must be supported) [6][18]. Vitest for RN is not the supported path — keep Jest for native packages.
StoriesStorybook 10 (ESM-only; Node 20.16+/22.19+/24+; Vitest 4 addon; sb.mock) [15]@storybook/react-native v10 on-device + @storybook/react-native-web-vite (10.4.x, Aug 2026) sharing the same stories; keep all storybook majors aligned [16]
Visual regressionChromatic (Free 5,000 snapshots/mo, Starter $179/mo 35k, Pro $399/mo 85k, OSS plan on request; TurboSnap on all plans) or self-hosted Loki / Vitest toMatchScreenshot [22][17]Storybook RN-web on Chromatic; device screenshots via Maestro
E2EPlaywright (@axe-core/playwright for a11y, offline via context.setOffline(true), PWA install/service-worker checks)Maestro (YAML flows; Expo EAS Workflows has a first-class maestro job; free CLI, paid cloud) [20]. Detox 20 supports RN 0.77–0.84 with New Arch but Expo support is community-only [19].
a11yaxe (web), RNTL accessible-name queries + react-native-a11y lint
i18n/RTLPlaywright projects with locale: 'ar' and I18nManager.forceRTL in Storybook decorators; snapshot ar/en/fr; ICU message lint
Offline/low-endPlaywright throttling + fake-indexeddb in Vitest; Android emulator with 1 GB RAM profile in Maestro on CI

11. Supply chain, security, CI

  • Threat model (2025–26): Shai-Hulud (Sept 8 2025: chalk, debug, 500+ packages; token-stealing, self-propagating via stolen npm tokens; 2.0 in Nov 2025 with 796 packages and preinstall payloads) and Nx s1ngularity (Aug 2025, stolen publish token) [27]. Countermeasures now default in pnpm 11: minimumReleaseAge (1 day; the docs suggest 10080 = 1 week for stricter shops), minimumReleaseAgeExclude for your own scope, allowBuilds allow-list instead of dangerouslyAllowAllBuilds, blockExoticSubdeps, trustPolicy: no-downgrade [24][25].
  • CI hygiene: pin every Action to a full SHA and let Renovate bump them; permissions: contents: read by default; OIDC-only publish; GitHub runners default to Node 24 from June 16 2026 (Node 20 removed fall 2026) so use actions/checkout@v5, actions/setup-node@v5, pnpm/action-setup@v4 [36]; run zizmor (Actions linter), pnpm audit (GHSA-based in pnpm 11), Socket (or OpenSSF Scorecard) on PRs; Dependabot security alerts on, Renovate for updates (grouped, minimumReleaseAge mirror, lockfile maintenance, automerge patch after checks).
  • Repo files: SECURITY.md (private vulnerability reporting via GitHub Security Advisories), CODEOWNERS, signed commits/tags, .github/workflows/release.yml gated on environment: npm with required reviewers.
  • CI matrix: ubuntu (Node 22, 24) for lint/type/test/build/publint/attw/size-limit; Node 20.19 smoke for consumers still on RN 0.85 tooling; macOS runner only for the Expo example build/Maestro iOS; Android emulator job on ubuntu (reactivecircus/android-emulator-runner) for Maestro; TS 7 preview job allowed to fail.
  • Bundle/perf budgets: size-limit (@size-limit/preset-small-lib, andresz1/size-limit-action PR comments) with per-entry gz budgets (e.g., @rasd-forms/core ≤ 25 kB, @rasd-forms/react ≤ 60 kB, builder lazy-chunked); publint + @arethetypeswrong/cli on npm pack output; Lighthouse CI on the Vite example (PWA installable, TTI on "Moto G4" throttling) [35][38].

Comparison tables

Monorepo/task runners

pnpm ws + Turborepo 2.10Nx 23
Node≥22 (pnpm 11)≥22
Configturbo.json(c), composable per packagenx.json, project.json/inferred
Remote cacheVercel or self-hostedNx Cloud
Catalogspnpm catalogs (Turbo 2.7 lockfile parser)pnpm catalogs (Nx 22+)
Fitsmall/medium library reposlarge polyglot orgs

RN E2E

MaestroDetox 20
SetupCLI only, YAML flowsnative integration, per-RN-version support (0.77–0.84)
Expodocumented in EAS Workflowscommunity-only
Sync modelblack-box + waitsgrey-box synchronisation
CostCLI free; cloud paidfree

Implications & recommendations for Rasd Forms

  1. Repo layout (pnpm 11 workspaces + Turborepo 2.10):
    rasd-forms/
    pnpm-workspace.yaml # catalog: react, react-native, expo, zod, typescript...
    turbo.jsonc
    packages/
    core/ @rasd-forms/core (JSON schema types, validation, logic/skip engine, i18n runtime; framework-free; tsdown)
    react/ @rasd-forms/react (web renderer, hooks; tsdown; conditions: browser/default)
    native/ @rasd-forms/native (RN renderer; bob; peers react-native, expo?)
    builder/ @rasd-forms/builder (drag-and-drop builder, web; tsdown; lazy chunks)
    storage-web/ @rasd-forms/storage-idb (IndexedDB adapter)
    storage-native/ @rasd-forms/storage-sqlite (expo-sqlite / op-sqlite adapters; bob)
    sync/ @rasd-forms/sync (queue, conflict rules, transport)
    themes/ @rasd-forms/theme-{default,unrwa-like,...}
    license/ @rasd-forms/license (offline-verifiable token check)
    eslint-config/, tsconfig/, tsdown-config/ (internal, private)
    apps/
    docs/ (Docusaurus 3.10, ar/en, RTL) example-vite/ example-next/ example-expo/ storybook-web/
    .changeset/ .github/workflows/ SECURITY.md CODEOWNERS
  2. Module format: ESM-only, "type": "module", extension-less .js output, named exports only, sideEffects: false except CSS/theme files; add a "rasd-source" condition for in-repo dev, resolved via TS customConditions, Metro unstable_conditionNames/react-native-monorepo-config, and Vitest resolve.conditions.
  3. Platform split by package, not by extension: @rasd-forms/react (web) and @rasd-forms/native share @rasd-forms/core; a small @rasd-forms/ui facade may map react-native condition → native entry and default → web entry, but keep separate entry files because Metro will not expand .native.js inside exports.
  4. TypeScript: TS 6.0 with strict, isolatedDeclarations, verbatimModuleSyntax, moduleResolution: bundler; run TS 7 preview as an advisory CI job; no react-native/Libraries/* deep imports (breaks on RN 0.87).
  5. Build: tsdown for web/core (unbundle: true for tree-shaking-friendly per-file output, dts: { isolatedDeclarations: true }), bob for native packages (module + typescript targets, esm: true), React Compiler precompiled with target: '19'; verify each tarball with publint and attw --pack.
  6. Peers: react ^19, react-dom ^19 (web pkgs), react-native >=0.81 (native), optional expo >=54, expo-sqlite, @op-engineering/op-sqlite; test matrix RN 0.81 / 0.85 / 0.87 and Expo SDK 54 / 56.
  7. Release: Changesets fixed group; OIDC trusted publishing per package (public repo, id-token: write, npm ≥11.5.1, Node 24 runner), provenance on, tokens disallowed, publishConfig.access: public; commercial packages published to the same public scope with a license-token runtime check (per doc 06) rather than a private registry, unless the pro builder must remain source-closed (then GitHub Packages/private npm with granular tokens, no OIDC provenance).
  8. Testing: Vitest 4 (jsdom for logic, browser mode for components) + RTL + vitest-axe; Jest + RNTL 14 for native; Storybook 10 with RN-web-vite for a single story set; Chromatic Free/OSS for visual diffs (upgrade only if >5k snapshots/mo); Playwright projects for en/ar (RTL) and offline mode; Maestro flows on the Expo example on EAS Workflows or CI emulators; explicit low-end Android profile.
  9. Lint/format: Biome (format + organise imports) and ESLint 9 flat (typescript-eslint v8 projectService, react-hooks v6, jsx-a11y, react-native-a11y, license-header rule); commitlint conventional commits; lefthook pre-commit running biome check + eslint --cache on staged files.
  10. Security posture: pnpm 11 defaults kept (minimumReleaseAge 1 day, allowBuilds allow-list), Renovate with minimumReleaseAge ≥3 days and grouped RN/Expo bumps, Socket + pnpm audit, SHA-pinned Actions, zizmor, OpenSSF Scorecard badge, pnpm sbom attached to GitHub Releases, SECURITY.md with 90-day disclosure policy — this is a credibility signal for UN/NGO procurement.
  11. Docs: Docusaurus 3.10 with i18n (en, ar RTL, fr), versioned docs per minor, TypeDoc→markdown API pages, live examples via @docusaurus/theme-live-codeblock, and a "Compatibility matrix" page (RN/Expo/React/Node) regenerated from the CI matrix.
  12. Budgets: size-limit gz budgets per entry with PR comments; Lighthouse CI PWA checks; Hermes bytecode size of the Expo example tracked; performance tests for 500-question forms on Android emulator (rendering + IndexedDB/SQLite write throughput).
  13. Error-reporting hooks: expose an onError/reportError hook in @rasd-forms/core (no built-in Sentry dependency), tag errors with schema/version, and rely on RN 0.82+ behaviour that unhandled promise rejections surface as console.error — sync code must catch and route through the hook.

Sources

  1. react-native-builder-bob — ESM support docs (GitHub docs/pages/esm.md): https://github.com/callstack/react-native-builder-bob/blob/main/docs/pages/esm.md (accessed 2026-08-15)
  2. react-native-builder-bob 0.40.0 release (ESM-only default template): https://github.com/callstack/react-native-builder-bob/releases/tag/react-native-builder-bob@0.40.0 (accessed 2026-08-15)
  3. React Native 0.82 — A New Era (Oct 8 2025): https://reactnative.dev/blog/2025/10/08/react-native-0.82 (accessed 2026-08-15)
  4. React Native blog index (0.80–0.87 dates): https://reactnative.dev/blog (accessed 2026-08-15)
  5. React Native 0.87 — Strict TypeScript API, Metro 0.87, SwiftPM, AGP 9 (Aug 11 2026): https://reactnative.dev/blog/2026/08/11/react-native-0.87 (accessed 2026-08-15)
  6. React Native 0.85 — Animation backend, @react-native/jest-preset, Node ≥20.19.4 (Apr 7 2026): https://reactnative.dev/blog/2026/04/07/react-native-0.85 (accessed 2026-08-15)
  7. Expo SDK 56 changelog (May 21 2026): https://expo.dev/changelog/sdk-56 (accessed 2026-08-15)
  8. Announcing TypeScript 6.0 (Mar 23 2026): https://devblogs.microsoft.com/typescript/announcing-typescript-6-0/ (accessed 2026-08-15)
  9. tsdown — Migrate from tsup: https://tsdown.dev/guide/migrate-from-tsup (accessed 2026-08-15)
  10. tsup repository README (maintenance notice): https://github.com/egoist/tsup (accessed 2026-08-15)
  11. Metro — Package Exports docs: https://metrobundler.dev/docs/package-exports/ (accessed 2026-08-15)
  12. GitHub Changelog — npm trusted publishing with OIDC is generally available (Jul 31 2025): https://github.blog/changelog/2025-07-31-npm-trusted-publishing-with-oidc-is-generally-available/ (accessed 2026-08-15)
  13. npm Docs — Trusted publishing for npm packages: https://docs.npmjs.com/trusted-publishers/ (accessed 2026-08-15)
  14. Phil Nash — Things you need to do for npm trusted publishing to work (Jan 28 2026): https://philna.sh/blog/2026/01/28/trusted-publishing-npm/ (accessed 2026-08-15)
  15. Storybook 10 announcement (ESM-only): https://storybook.js.org/blog/storybook-10/ (accessed 2026-08-15)
  16. storybookjs/react-native README (v10): https://github.com/storybookjs/react-native (accessed 2026-08-15)
  17. Vitest 4.0 is out (Oct 22 2025): https://vitest.dev/blog/vitest-4 (accessed 2026-08-15)
  18. React Native Testing Library releases (v14.0.0 Jun 5 2026): https://github.com/callstack/react-native-testing-library/releases (accessed 2026-08-15)
  19. Detox — Environment setup (RN 0.77–0.84, Expo community-only): https://wix.github.io/Detox/docs/introduction/environment-setup/ (accessed 2026-08-15)
  20. Expo — E2E tests with Maestro on EAS Workflows: https://docs.expo.dev/eas/workflows/examples/e2e-tests/ (accessed 2026-08-15)
  21. Mintlify pricing: https://mintlify.com/pricing (accessed 2026-08-15)
  22. Chromatic pricing: https://www.chromatic.com/pricing (accessed 2026-08-15)
  23. Biome v2.5 — 500 lint rules, plugin code fix, cross-file linting (Jun 5 2026): https://biomejs.dev/blog/biome-v2-5/ (accessed 2026-08-15)
  24. pnpm 11.0 release notes (Apr 28 2026): https://pnpm.io/blog/releases/11.0 (accessed 2026-08-15)
  25. pnpm — Mitigating supply chain attacks: https://pnpm.io/supply-chain-security (accessed 2026-08-15)
  26. pnpm issue #11513 — OIDC publish fails on pnpm 11: https://github.com/pnpm/pnpm/issues/11513 (accessed 2026-08-15)
  27. Unit 42 — "Shai-Hulud" worm compromises npm ecosystem: https://unit42.paloaltonetworks.com/npm-supply-chain-attack/ (accessed 2026-08-15)
  28. React — Compiling Libraries (React Compiler): https://react.dev/reference/react-compiler/compiling-libraries (accessed 2026-08-15)
  29. React Compiler v1.0 (Oct 7 2025): https://react.dev/blog/2025/10/07/react-compiler-1 (accessed 2026-08-15)
  30. Rslib — Introducing Rslib: https://rslib.rs/blog/introducing-rslib (accessed 2026-08-15)
  31. Turborepo blog (2.5–2.10 release notes): https://turborepo.dev/blog (accessed 2026-08-15)
  32. Nx 23 release: https://nx.dev/blog/nx-23-release (accessed 2026-08-15)
  33. TypeDoc issue #3098 — TypeScript 7 (TS-Go) support: https://github.com/TypeStrong/typedoc/issues/3098 (accessed 2026-08-15)
  34. rushstack issue #4877 — API Extractor "Unknown compiler option 'isolatedDeclarations'": https://github.com/microsoft/rushstack/issues/4877 (accessed 2026-08-15)
  35. size-limit: https://github.com/ai/size-limit and https://github.com/andresz1/size-limit-action (accessed 2026-08-15)
  36. GitHub Changelog — Deprecation of Node 20 on GitHub Actions runners (Sept 19 2025; Node 24 default June 16 2026): https://github.blog/changelog/2025-09-19-deprecation-of-node-20-on-github-actions-runners/ (accessed 2026-08-15)
  37. Docusaurus blog (3.9 Sept 25 2025; 3.10 Apr 7 2026): https://docusaurus.io/blog (accessed 2026-08-15)
  38. publint rules: https://publint.dev/rules (accessed 2026-08-15)
  39. Anthony Fu — Move on to ESM-only (Feb 5 2025): https://antfu.me/posts/move-on-to-esm-only (accessed 2026-08-15)
  40. React 19.2 (Oct 1 2025) and 19.2.8 release (Jul 21 2026): https://react.dev/blog/2025/10/01/react-19-2 ; https://github.com/react/react/releases/tag/v19.2.8 (accessed 2026-08-15)
  41. Fumadocs — Comparisons: https://www.fumadocs.dev/docs/comparisons (accessed 2026-08-15)
  42. changesets/action issue #515 — separate publish workflow for OIDC: https://github.com/changesets/action/issues/515 (accessed 2026-08-15)
  43. Nx 22 release (pnpm catalogs): https://nx.dev/blog/nx-22-release (accessed 2026-08-15)
  44. tsdown — Declaration files (dts) options: https://tsdown.dev/options/dts (accessed 2026-08-15)