Axonpack
@axonpack/expo-devtoolsReference

createDevtoolsClient

Every configuration option, and every method on the client it returns.

import { createDevtoolsClient } from '@axonpack/expo-devtools';

export const devtools = createDevtoolsClient(config?);

Call it once, at module scope, and export the instance. Everything else hangs off it. Every option is optional, and the defaults are what most apps want.

Top level

OptionTypeDefaultWhat it does
defaultThemeThemeId'light'Which theme the panel opens with: a built-in or one of yours.
themesRecord<string, ThemeConfig>undefinedYour own themes: a base to inherit and the tokens to override.
webviewSourcesreadonly string[]undefinedNames of <WebView />s allowed to report in, for both the Network and Console tabs.

webviewSources uses a const type parameter, so the literal names flow into the WebView helpers' parameter types: passing an undeclared name is a compile error, and at runtime a message from an undeclared source is dropped.

Network

The switches name the kind of traffic, not the mechanism that carried it. A request is a request whether it went out through fetch, through XMLHttpRequest, from a JSI client or from inside a page.

OptionTypeDefaultWhat it does
network.httpbooleantrueCapture plain requests, by whatever transport they left on. Off also means no phase timing.
network.websocketbooleantrueCapture WebSocket connections and their messages, the app's own and a page's.
network.ssebooleantrueCapture server-sent event streams and their events, whichever client opened them.
network.disabledByDefaultbooleanfalseOpen the Network tab paused.

With sse off, the app's own stream is still recognised as one — its endless body has to be, or it would be read as a response — so the row remains and only the events are dropped. A page's stream has no request underneath it that anything here can see, so that one disappears entirely.

Console

OptionTypeDefaultWhat it does
console.capturebooleantrueMirror console.* into the Console tab, including from declared WebViews.
console.replbooleantrueShow the > prompt.
console.contextRecord<string, unknown>undefinedExtra names an expression can use, for example { store, queryClient }.
console.disabledByDefaultbooleanfalseOpen the Console tab paused. The prompt still works.

console.repl is not gated on __DEV__

It defaults to true in every build. Once init() has run the prompt is there, including in a release build, where it runs whatever is typed into it. Guard your init() call, or set console: { repl: false }.

Performance

OptionTypeDefaultWhat it does
performance.sampleIntervalMsnumber1000How often memory is sampled. Each read crosses into the engine, so keep it coarse.
performance.longTaskThresholdMsnumber150Only keep tasks that blocked the JS thread at least this long.
performance.interactionThresholdMsnumber100Only keep interactions at least this long, event to next paint.
performance.historySizenumber120How many memory samples, long tasks, user timings and interactions are kept.
performance.disabledByDefaultbooleantrueOpen the Performance tab paused. Defaults to on, since measuring costs something.

Storage

OptionTypeDefaultWhat it does
storage.adaptersStorageAdapterDefinition[]undefinedThe stores the Storage tab can see. Nothing is discovered automatically.
storage.maxKeysnumber1000Keys read per store before the tab stops and says how many it skipped.
storage.readOnlybooleanfalseBlanket read-only default; an individual adapter can still set its own.

See Storage adapters for how to build one.

Crash

Crash capture is the only part of this package that can run without init().

OptionTypeDefaultWhat it does
crash.enabledbooleantrueCapture at all.
crash.enableWhileDevtoolsDisabledbooleanfalseInstall the handlers when the client is constructed, so crashes are reported without init().
crash.handlers.jsErrorsbooleantrueThe ErrorUtils global handler — fatal and non-fatal JS errors.
crash.handlers.unhandledRejectionsbooleantrueUnhandled promise rejections, via the Hermes rejection tracker.
crash.handlers.nativeExceptionsbooleantrueUncaught Java/Kotlin and Objective-C exceptions, via the native module.
crash.popupDetail'auto' | 'full' | 'compact''auto'Which sheet a crash opens. 'auto' is the full sheet when the devtools are enabled, compact when not.
crash.disableDefaultLogBoxbooleanfalseUninstall React Native's LogBox, so a JS error is reported here and nowhere else.
crash.breadcrumbsbooleantrueAttach the recent console and network entries to each record.
crash.maxBreadcrumbsnumberHow many breadcrumbs are attached.
crash.maxRecordsnumber25Reports kept in memory.
crash.persistNonFatalbooleanfalseAlso write non-fatal records to disk.
crash.redact(record: CrashRecord) => CrashRecord | nullundefinedRuns before the record reaches the store, the disk or onCrash. Return null to drop it.
crash.onCrash(record: CrashRecord) => voidundefinedYour own handler, after redact.

Before init(), an app relying on enableWhileDevtoolsDisabled alone installs nativeExceptions only, whatever the other two say: the JS tiers report errors the app survived, which is a developer's concern, and the sheet there is in front of a user. A fatal JS error still arrives, because React Native turns it into a native exception on the way to killing the process.

disableDefaultLogBox uninstalls LogBox rather than muting it, which takes the yellow warning toasts with it — LogBox is one component and the two cannot be separated. Warnings are still captured by the Console tab. It only does anything in development; LogBox is already an empty stub in a release build.

Client methods

MemberWhat it does
init()Installs everything: the fetch/XHR patches, the console patch, the REPL context, the performance collectors, your storage adapters, and your themes. Until this runs, nothing is captured and no store is read. Call once, as early as possible.
mark(name, options?)Records a user-timing mark. options: { detail?, startTime? }.
measure(name, startOrOptions?, endMark?)Records a measure. Second argument is a start-mark name or { start?, end?, duration?, detail? }. Passing start, end and duration together throws, since they can disagree.
clearMarks(name?)Drops recorded marks, all of them or one name.
clearMeasures(name?)Drops recorded measures, all of them or one name.
setCrashContext(context)Extra keys attached to every crash record from here on — user id, route, feature flags.
getWebViewInjectedJavaScriptBeforeContentLoaded(source)The script to hand a <WebView />'s injectedJavaScriptBeforeContentLoaded. Covers both requests and console output.
handleWebViewMessage(event)Feed a <WebView />'s onMessage events here. Returns true when it consumed one.
getWebViewRef(source)A ref to attach to the <WebView />, so a throttle change reaches an already-open page.
getWebViewUserAgent()The current user-agent override, for the userAgent prop.
shouldAllowWebViewRequestFor onShouldStartLoadWithRequest. Blocks navigation while Offline is on.
networkLogStore, networkConditionsStore, consoleLogStore, storageStore, crashStoreThe underlying stores, if you want to read or drive them yourself.

User timing

devtools.mark('checkout');
await buildCart();
devtools.measure('checkout'); // measures from the mark of the same name

measure follows the W3C User Timing signatures, and calls are forwarded to the real performance.mark and performance.measure too, so the entries exist on the platform timeline as well. Nothing is observed from that timeline, which is why React's own internal measures never appear in the list.

On this page