[ Documentation ]
RNXConfig

RNXConfig

The main configuration object for rnx projects. Import from rnxsim/config:

import { defineConfig } from 'rnxsim/config'
export default defineConfig({
modules: { ... },
turboModules: { ... },
nativeModules: { ... },
env: { ... },
settings: { ... },
initialState: { ... },
})

defineConfig is a typed pass-through; the file may also be plain CJS:

/** @type {import('rnxsim/config').RNXConfig} */
module.exports = { env: { ... } }

Module slots

There are three separate slots because the runtime resolves each differently.

modules

Record<string, ModuleResolution>: overrides for any Metro module by path fragment. The key is matched against the Metro module name as a path fragment, not a strict package name. A key like react-native-widgetkit matches …/node_modules/react-native-widgetkit/index.js; a key like dist/assets/config.json matches a deep file path inside a module.

Keys are sorted longest-first, so subpath keys can override package-level keys.

turboModules

Record<string, NativeModuleResolution>: modules served via the TurboModuleRegistry surface. Keyed by the TurboModule name the guest bundle requests (e.g. RNCAsyncStorage), not by JS module path.

nativeModules

Record<string, NativeModuleResolution>: modules served via the legacy NativeModules surface. Keyed by the native module name the guest bundle requests (e.g. RNKeychainManager).

Module resolution kinds

type ModuleResolution =
| 'noop' // stub as an empty module
| false // disable rnx's builtin stub, use the original
| { use: string } // redirect to an existing compat stub by name
| { file: string } // resolve to a project-local file
| { inline: Record<string, any> } // inline static object as the module exports

turboModules and nativeModules additionally accept a direct native-module object (any shape) for non-URL configs:

type NativeModuleResolution = ModuleResolution | Record<string, any>

env

Record<string, string>: environment variables installed as process.env.* inside the guest runtime.

Caveat: many React Native projects read env vars at bundle time via Babel plugins (react-native-dotenv, Expo’s EXPO_PUBLIC_* inliner, …). Those values are already baked into the bundle that Metro served. env here only sets process.env.* for code that reads env at runtime; code that already got its values inlined at bundle time will not see new values. To change those, rebuild the bundle with the new env and reload.

settings

Partial<SettingsValues>: simulator settings overrides (device model, appearance, network, locale, chrome). See settings schema.

initialState

Initial app state passed to the simulator:

{
authenticated?: boolean
locale?: string
colorScheme?: 'light' | 'dark' | 'auto'
featureFlags?: Record<string, boolean | string | number>
[key: string]: any
}

How the config reaches the runtime

The config travels as the ?rnxConfig=<json> URL query param.

  • +
    applyRNXConfigToUrl(url, config) serializes into the param.
  • +
    readRNXConfigFromSearchParams(searchParams) deserializes inside the engine.
  • +
    The engine preloads any { file: '…' } modules by fetching them through the /__sootsim-replacement-module dev-server middleware (one project-local source file, whose imports are restricted to react, react-native, and the packages the simulator bridges for this app).
  • +
    The bundle loader matches modules keys against every __d() factory registration and serves the configured resolution instead.

mergeRNXConfig(base, override) shallowly merges two configs by slot.