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
- 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]. - Ship ESM-only from Metro-friendly, extension-less
.jsoutput. react-native-builder-bob's default template dropped dual CJS/ESM in 0.40.0 (Apr 2025); Metro honoursexportsby default since Metro 0.82 / RN 0.79; Node 20.19+/22.12+ canrequire()ESM. Dual publishing now mostly creates the "dual package hazard" (two copies of React context/state) [1][2][11][39]. exportsmap withreact-nativecondition first,typesfirst-within-branch,defaultlast, plus a private "source" condition for the monorepo. When a subpath matchesexports, Metro no longer expands.native.js/.ios.jsextensions, so platform splitting must be done by separate packages or by pointingreact-nativecondition targets at native entry files [1][11][38].- 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].
- TypeScript 6.0 (Mar 23 2026) flipped defaults (
strict,module: esnext,target: es2025,types: []) and deprecatesbaseUrl,moduleResolution: node,target: es5; TS 7 (Go port) is in preview. AdoptisolatedDeclarationsnow so tsdown/rslib/oxc can emit.d.tswithout the type-checker; TypeDoc will feature-freeze once TS 7 ships and API Extractor has historically choked onisolatedDeclarations[8][33][34]. - 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 vianpm publish/changeset publish[12][13][14][26]. - 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) defaultsminimumReleaseAge=1440min,blockExoticSubdeps=true,strictDepBuilds,allowBuildsallow-list; pair with Socket/GHSA audit, SHA-pinned Actions and Node 24 runners (default from June 16 2026) [24][25][27][36]. - Testing stack: Vitest 4 (Oct 22 2025; browser mode stable,
toMatchScreenshot), RTL for web, RNTL v14 (Jun 5 2026; React ≥19, RN ≥0.78, asyncrender), Storybook 10 (ESM-only;@storybook/react-nativev10 +@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]. - Lint/format: ESLint 9 flat config + typescript-eslint v8 (
projectService) remains necessary foreslint-plugin-react-hookscompiler 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]. - Docs: Docusaurus 3.10 (Apr 2026, last 3.x,
future.v4flag) 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)
| Layer | Current (Aug 2026) | Notes for a library |
|---|---|---|
| React | 19.2 (Oct 2025), patch 19.2.8 (Jul 21 2026) [40] | Activity, useEffectEvent, owner stacks. No React 20 announced. |
| React Native | 0.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). |
| Expo | SDK 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. |
| Hermes | V1 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]. |
| TypeScript | 6.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. |
| Node | Node 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);allowBuildsmap replacesonlyBuiltDependencies/neverBuiltDependencies; pnpm settings move from.npmrctopnpm-workspace.yaml; nativepublish/dist-tag/deprecate(npm CLI fallback removed); newpnpm 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
tsconfigfor packages:"strict": true,"moduleResolution": "bundler"(bob explicitly warns againstnode16/nodenextbecause 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]. isolatedDeclarationsforces explicit return types on exports; payoff is that tsdown/rslib/oxc-transform generate.d.tsin milliseconds withouttsc[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
| Tool | Engine | Status (Aug 2026) | Fit for Rasd Forms |
|---|---|---|---|
| tsup | esbuild | README: "This project is not actively maintained anymore. Please consider using tsdown instead." [10] | Do not start new packages on it. |
| tsdown | Rolldown (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. |
| rslib | Rspack | 0.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 + plugins | JS | Mature; more config; slower dts | Only for exotic needs. |
| unbuild | rollup/mkdist | UnJS; "stub" mode nice for monorepos | Fine, but tsdown covers it. |
| react-native-builder-bob 0.40+ | Babel + tsc | Default 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, thenimport/require(whichever the importing file used, never both), thendefault; enabled by default since Metro 0.82 (RN 0.79) withunstable_enablePackageExportsstill 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 — nosourceExtsor.ios.js/.native.jsexpansion; addingexportsto an existing package is a breaking change unless every prior deep import is re-listed [11]. - publint:
typesfirst,defaultlast,modulebeforerequire, dual formats need separate.d.mts/.d.cts, preferexports/importsbrowser conditions over the legacybrowserfield, 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
srcin 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) aspeerDependencieswithpeerDependenciesMeta.optional = truefor platform-only ones; pnpm 11pnpm peers checkandpnpm.peerDependencyRuleshelp 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 — noreact-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 addreact-compiler-runtimeas 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 manualuseMemoout 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@v1opening 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--provenanceorpublishConfig.provenance: truein some cases andrepository.urlmust exactly matchgit+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) withnpmupgraded, 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 commercialLICENSE-COMMERCIALfor pro packages) and SPDX headers// SPDX-License-Identifier: Apache-2.0enforced by an ESLintheaderrule or Biome plugin; runpnpm sbomandlicense-checker-rseidelsohnin release CI [24].
8. API docs and docs sites
- TypeDoc 0.28.x (
typedoc-plugin-markdownfor 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.mdreports (great for enforcing public-API review in PRs) but hadisolatedDeclarationsissues [34]. Practical: TypeDoc for docs,api-extractor run --localin CI as an API-report guard, verify against yourtsconfigfirst. - Docs site comparison [21][37][41]:
| Option | Stack | Pros | Cons | Cost |
|---|---|---|---|---|
| Docusaurus 3.10 | React/MDX, Rspack | i18n + RTL out of the box, versioned docs, Algolia DocSearch v4/AskAI, huge ecosystem; 3.10 = last 3.x, future.v4 flag | Slower dev server than Astro; opinionated | Free |
| Fumadocs | Next.js | Best if docs live in a Next.js app; OpenAPI pages; very customisable | More React/Next knowledge; i18n DIY | Free |
| Starlight | Astro | Fastest static output, built-in i18n | Not React-native-friendly for live component demos (islands only) | Free |
| Mintlify | Hosted | Polished, AI assistant on paid tiers | Vendor 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.jsat root, per-package overrides),typescript-eslintv8 withparserOptions.projectService: true(no per-packageprojectarrays 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
| Layer | Web | React Native |
|---|---|---|
| Unit/component | Vitest 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. |
| Stories | Storybook 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 regression | Chromatic (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 |
| E2E | Playwright (@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]. |
| a11y | axe (web), RNTL accessible-name queries + react-native-a11y lint | |
| i18n/RTL | Playwright projects with locale: 'ar' and I18nManager.forceRTL in Storybook decorators; snapshot ar/en/fr; ICU message lint | |
| Offline/low-end | Playwright 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 andpreinstallpayloads) 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),minimumReleaseAgeExcludefor your own scope,allowBuildsallow-list instead ofdangerouslyAllowAllBuilds,blockExoticSubdeps,trustPolicy: no-downgrade[24][25]. - CI hygiene: pin every Action to a full SHA and let Renovate bump them;
permissions: contents: readby default; OIDC-only publish; GitHub runners default to Node 24 from June 16 2026 (Node 20 removed fall 2026) so useactions/checkout@v5,actions/setup-node@v5,pnpm/action-setup@v4[36]; runzizmor(Actions linter),pnpm audit(GHSA-based in pnpm 11), Socket (or OpenSSF Scorecard) on PRs; Dependabot security alerts on, Renovate for updates (grouped,minimumReleaseAgemirror, lockfile maintenance, automerge patch after checks). - Repo files:
SECURITY.md(private vulnerability reporting via GitHub Security Advisories),CODEOWNERS, signed commits/tags,.github/workflows/release.ymlgated onenvironment: npmwith 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-actionPR comments) with per-entry gz budgets (e.g.,@rasd-forms/core≤ 25 kB,@rasd-forms/react≤ 60 kB, builder lazy-chunked);publint+@arethetypeswrong/clionnpm packoutput; Lighthouse CI on the Vite example (PWA installable, TTI on "Moto G4" throttling) [35][38].
Comparison tables
Monorepo/task runners
| pnpm ws + Turborepo 2.10 | Nx 23 | |
|---|---|---|
| Node | ≥22 (pnpm 11) | ≥22 |
| Config | turbo.json(c), composable per package | nx.json, project.json/inferred |
| Remote cache | Vercel or self-hosted | Nx Cloud |
| Catalogs | pnpm catalogs (Turbo 2.7 lockfile parser) | pnpm catalogs (Nx 22+) |
| Fit | small/medium library repos | large polyglot orgs |
RN E2E
| Maestro | Detox 20 | |
|---|---|---|
| Setup | CLI only, YAML flows | native integration, per-RN-version support (0.77–0.84) |
| Expo | documented in EAS Workflows | community-only |
| Sync model | black-box + waits | grey-box synchronisation |
| Cost | CLI free; cloud paid | free |
Implications & recommendations for Rasd Forms
- Repo layout (pnpm 11 workspaces + Turborepo 2.10):
rasd-forms/pnpm-workspace.yaml # catalog: react, react-native, expo, zod, typescript...turbo.jsoncpackages/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
- Module format: ESM-only,
"type": "module", extension-less.jsoutput, named exports only,sideEffects: falseexcept CSS/theme files; add a"rasd-source"condition for in-repo dev, resolved via TScustomConditions, Metrounstable_conditionNames/react-native-monorepo-config, and Vitestresolve.conditions. - Platform split by package, not by extension:
@rasd-forms/react(web) and@rasd-forms/nativeshare@rasd-forms/core; a small@rasd-forms/uifacade may mapreact-nativecondition → native entry anddefault→ web entry, but keep separate entry files because Metro will not expand.native.jsinsideexports. - TypeScript: TS 6.0 with
strict,isolatedDeclarations,verbatimModuleSyntax,moduleResolution: bundler; run TS 7 preview as an advisory CI job; noreact-native/Libraries/*deep imports (breaks on RN 0.87). - Build: tsdown for web/core (
unbundle: truefor tree-shaking-friendly per-file output,dts: { isolatedDeclarations: true }), bob for native packages (module+typescripttargets,esm: true), React Compiler precompiled withtarget: '19'; verify each tarball withpublintandattw --pack. - Peers:
react ^19,react-dom ^19(web pkgs),react-native >=0.81(native), optionalexpo >=54,expo-sqlite,@op-engineering/op-sqlite; test matrix RN 0.81 / 0.85 / 0.87 and Expo SDK 54 / 56. - 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). - 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. - 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 runningbiome check+eslint --cacheon staged files. - Security posture: pnpm 11 defaults kept (
minimumReleaseAge1 day,allowBuildsallow-list), Renovate withminimumReleaseAge≥3 days and grouped RN/Expo bumps, Socket +pnpm audit, SHA-pinned Actions,zizmor, OpenSSF Scorecard badge,pnpm sbomattached to GitHub Releases,SECURITY.mdwith 90-day disclosure policy — this is a credibility signal for UN/NGO procurement. - Docs: Docusaurus 3.10 with
i18n(en,arRTL,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. - 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).
- Error-reporting hooks: expose an
onError/reportErrorhook 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 asconsole.error— sync code must catch and route through the hook.
Sources
- 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) - 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)
- 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)
- React Native blog index (0.80–0.87 dates): https://reactnative.dev/blog (accessed 2026-08-15)
- 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)
- 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) - Expo SDK 56 changelog (May 21 2026): https://expo.dev/changelog/sdk-56 (accessed 2026-08-15)
- Announcing TypeScript 6.0 (Mar 23 2026): https://devblogs.microsoft.com/typescript/announcing-typescript-6-0/ (accessed 2026-08-15)
- tsdown — Migrate from tsup: https://tsdown.dev/guide/migrate-from-tsup (accessed 2026-08-15)
- tsup repository README (maintenance notice): https://github.com/egoist/tsup (accessed 2026-08-15)
- Metro — Package Exports docs: https://metrobundler.dev/docs/package-exports/ (accessed 2026-08-15)
- 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)
- npm Docs — Trusted publishing for npm packages: https://docs.npmjs.com/trusted-publishers/ (accessed 2026-08-15)
- 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)
- Storybook 10 announcement (ESM-only): https://storybook.js.org/blog/storybook-10/ (accessed 2026-08-15)
- storybookjs/react-native README (v10): https://github.com/storybookjs/react-native (accessed 2026-08-15)
- Vitest 4.0 is out (Oct 22 2025): https://vitest.dev/blog/vitest-4 (accessed 2026-08-15)
- React Native Testing Library releases (v14.0.0 Jun 5 2026): https://github.com/callstack/react-native-testing-library/releases (accessed 2026-08-15)
- Detox — Environment setup (RN 0.77–0.84, Expo community-only): https://wix.github.io/Detox/docs/introduction/environment-setup/ (accessed 2026-08-15)
- Expo — E2E tests with Maestro on EAS Workflows: https://docs.expo.dev/eas/workflows/examples/e2e-tests/ (accessed 2026-08-15)
- Mintlify pricing: https://mintlify.com/pricing (accessed 2026-08-15)
- Chromatic pricing: https://www.chromatic.com/pricing (accessed 2026-08-15)
- 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)
- pnpm 11.0 release notes (Apr 28 2026): https://pnpm.io/blog/releases/11.0 (accessed 2026-08-15)
- pnpm — Mitigating supply chain attacks: https://pnpm.io/supply-chain-security (accessed 2026-08-15)
- pnpm issue #11513 — OIDC publish fails on pnpm 11: https://github.com/pnpm/pnpm/issues/11513 (accessed 2026-08-15)
- Unit 42 — "Shai-Hulud" worm compromises npm ecosystem: https://unit42.paloaltonetworks.com/npm-supply-chain-attack/ (accessed 2026-08-15)
- React — Compiling Libraries (React Compiler): https://react.dev/reference/react-compiler/compiling-libraries (accessed 2026-08-15)
- React Compiler v1.0 (Oct 7 2025): https://react.dev/blog/2025/10/07/react-compiler-1 (accessed 2026-08-15)
- Rslib — Introducing Rslib: https://rslib.rs/blog/introducing-rslib (accessed 2026-08-15)
- Turborepo blog (2.5–2.10 release notes): https://turborepo.dev/blog (accessed 2026-08-15)
- Nx 23 release: https://nx.dev/blog/nx-23-release (accessed 2026-08-15)
- TypeDoc issue #3098 — TypeScript 7 (TS-Go) support: https://github.com/TypeStrong/typedoc/issues/3098 (accessed 2026-08-15)
- rushstack issue #4877 — API Extractor "Unknown compiler option 'isolatedDeclarations'": https://github.com/microsoft/rushstack/issues/4877 (accessed 2026-08-15)
- size-limit: https://github.com/ai/size-limit and https://github.com/andresz1/size-limit-action (accessed 2026-08-15)
- 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)
- Docusaurus blog (3.9 Sept 25 2025; 3.10 Apr 7 2026): https://docusaurus.io/blog (accessed 2026-08-15)
- publint rules: https://publint.dev/rules (accessed 2026-08-15)
- Anthony Fu — Move on to ESM-only (Feb 5 2025): https://antfu.me/posts/move-on-to-esm-only (accessed 2026-08-15)
- 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)
- Fumadocs — Comparisons: https://www.fumadocs.dev/docs/comparisons (accessed 2026-08-15)
- changesets/action issue #515 — separate publish workflow for OIDC: https://github.com/changesets/action/issues/515 (accessed 2026-08-15)
- Nx 22 release (pnpm catalogs): https://nx.dev/blog/nx-22-release (accessed 2026-08-15)
- tsdown — Declaration files (dts) options: https://tsdown.dev/options/dts (accessed 2026-08-15)