Axonpack

Upgrading

What moved where in 3.0, and the two lines most apps have to change.

2.x to 3.0

The setup API is replaced. There is no client object any more, so nothing has to be created at module scope, exported, or passed to the components that need it. One provider starts the devtools and hosts the panel, and one flag in its config decides whether any of it runs.

Nothing about the panel itself changed, and nothing inside config changed: network, console, performance, storage and crash take the same fields they always did, as do the storage adapters and custom themes. If your app does not open a <WebView /> or call mark, this is a two-step upgrade.

1. Replace the client, the init call and the overlay

// before
export const devtools = createDevtoolsClient({ defaultTheme: 'dark' });
if (__DEV__) devtools.init();

export default function App() {
  return (
    <>
      <YourApp />
      {__DEV__ && <DevtoolsOverlay />}
    </>
  );
}
// after
export default function App() {
  return (
    <DevtoolsProvider config={{ enabled: __DEV__, defaultTheme: 'dark' }}>
      <YourApp />
    </DevtoolsProvider>
  );
}

The condition you used to guard the mount with becomes enabled. Whatever expression it was still works: __DEV__, an environment variable, a value you fetch for one user. See Leaving it in production.

A long config is easier to keep in its own file, exactly as the client used to be:

devtools.ts
import type { DevtoolsConfig } from '@axonpack/expo-devtools';

export const devtoolsConfig = {
  enabled: __DEV__,
  // ...everything you passed to createDevtoolsClient
} satisfies DevtoolsConfig;

2. Move your in-app browser wiring to the hook

// before
<WebView
  ref={devtools.getWebViewRef('checkout')}
  userAgent={devtools.getWebViewUserAgent()}
  injectedJavaScriptBeforeContentLoaded={devtools.getWebViewInjectedJavaScriptBeforeContentLoaded(
    'checkout'
  )}
  onShouldStartLoadWithRequest={devtools.shouldAllowWebViewRequest}
  onMessage={devtools.handleWebViewMessage}
  source={{ uri }}
/>
// after
const devtoolsWebView = useDevtoolsWebView('checkout');

<WebView {...devtoolsWebView} source={{ uri }} />;

Delete webviewSources from your config. A name is no longer declared anywhere: it is whatever you hand the hook, and it only labels that page's rows. See In-app browsers.

3. Import devtools where you used the client

mark, measure, setCrashContext and the stores kept their names and signatures. They come from the package now instead of from an instance you made:

// before
import { devtools } from '../devtools';

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

Everything that moved

2.x3.0
createDevtoolsClient(config)<DevtoolsProvider config={config}>
devtools.init()Gone. The provider starts as it renders
<DevtoolsOverlay {...props} />The same props, on <DevtoolsProvider>
Guarding the overlay mountconfig.enabled
config.webviewSourcesGone. Pass the name to useDevtoolsWebView
devtools.getWebViewInjectedJavaScriptBeforeContentLoadeduseDevtoolsWebView(...).injectedJavaScriptBeforeContentLoaded
devtools.handleWebViewMessageuseDevtoolsWebView(...).onMessage
devtools.getWebViewRefuseDevtoolsWebView(...).ref
devtools.getWebViewUserAgent()useDevtoolsWebView(...).userAgent
devtools.shouldAllowWebViewRequestuseDevtoolsWebView(...).onShouldStartLoadWithRequest
devtools.mark / measure / clearMarks / clearMeasuresThe same, on the exported devtools
devtools.setCrashContextThe same, on the exported devtools
devtools.networkLogStore and the other storesThe same, on the exported devtools
DevtoolsClientConfigDevtoolsConfig
DevtoolsOverlayPropsDevtoolsProviderProps
StartupTiming.initCalledStartupTiming.devtoolsStart

Two things that behave differently

Capture starts at the provider's first render, not at module evaluation. It is still before any child mounts, so the first screen's requests are caught, but a request fired while modules are evaluating now happens too early to see. If you moved init() ahead of Expo Router's entry file to widen that window, that trick is gone: undo it and let the root layout hold the provider.

crash.enableWhileDevtoolsDisabled installs at that same first render, rather than when a client was constructed. It still captures crashes with enabled: false, which is the whole point of the flag, and it still reports only the crashes that end the app.

New in 3.0

  • useDevtoolsPanel() opens and closes the panel from your own UI.
  • showFloatingButton={false} hides the launcher button and leaves the panel working.

Next step

On this page