Skip to content

RFC: 3P Extensions API for React Native DevTools panels#979

Draft
huntie wants to merge 3 commits intoreact-native-community:mainfrom
huntie:rfc-load-devtools-extensions
Draft

RFC: 3P Extensions API for React Native DevTools panels#979
huntie wants to merge 3 commits intoreact-native-community:mainfrom
huntie:rfc-load-devtools-extensions

Conversation

@huntie
Copy link
Collaborator

@huntie huntie commented Feb 7, 2026

Note

🚧 Draft proposal. No ETA, no committed plan to build this.

This RFC is linked to a speculative end-to-end implementation that I've been aggressively exploring today. Many more questions need answering around internal APIs implementing this — this doc only focuses on the outer public APIs we could introduce as an integration point for Frameworks.

I'm publishing this draft PR in the hope of early partner review / to pin a flag down on how feasible this should be to tackle when we can prioritise it.

@V3RON
Copy link

V3RON commented Feb 9, 2026

Hi @huntie 👋
It's great to see the growing interest in implementing full-fledged plugin support in React Native DevTools!

Your RFC is quite similar to a PoC I built a couple of months ago. The outcome was that I was able to load Chrome DevTools extensions into my Electron-based shell running React Native DevTools, with some additional code injected via Rozenite. I successfully ran Altair GraphQL and could see requests being sent from the device. No changes on the extension side were required.

If I remember correctly, this was possible thanks to nodeIntegrationInSubFrames, which allows synchronous code execution in an iframe's context before the main script loads. I have the code somewhere. I just need to dig it up.

CHROME_DEVTOOLS_EXTENSION.mp4

I'd be more than happy to share more details if you're interested in this approach.

@vonovak
Copy link
Member

vonovak commented Feb 12, 2026

Hello and thanks for writing this up, @huntie.
We've been prototyping extension support in Expo and have a working poc, so there's some experience to share. Our goal was mainly to get Expo DevTools plugins (which currently open as browser tabs), to open as panels inside RNDT for a more unified experience. It wasn't a goal to try arbitrary chrome extensions.

Overall we're aligned on most of the design. Some of the below refers to wip branches in your forks. A few areas worth discussing:

Where we align

  • Chrome Extension API subset as the contract, chrome.devtools.panels.create() as the entry point for registering panels - agree.
  • Integrator-configured extension list via createDevMiddleware(). We have done this with a similar shape. Expo can discover extensions via autolinking (expo-module.config.json → devtools.webpageRoot) and pass them to createDevMiddleware(). The manifest.json file lends itself to be discovered as well.
  • inspectedURL workaround. We arrived at the same solution of setting a synthetic URL.
  • panels.create path resolution. We also wrap panels.create to resolve relative page paths against the extension's base directory, since all extensions share the DevTools origin (unlike Chrome where each gets its own).
  • Explicit API surface restriction - DISALLOWED_API_NAMESPACES stripping is a good call.

chrome.devtools API injection

The postMessage approach is host-agnostic (browser / electron). The trade-off is that every extension must include the bootstrap snippet (postMessage('requestExtensionAPI') + eval the response). In Chrome, the embedder injects the API script via setInjectedScriptForOrigin and some C++ magic before the extension iframe's content loads — extensions don't (need to) know about the injection mechanism. We tried to preserve this.

We prototyped injection in the Electron shell: session.protocol.handle — intercept extension HTML requests and inject the script into before serving. It works, but there is raw HTML replacement (String.replace()) which is not great.

As mentioned, another option could be nodeIntegrationInSubFrames — an option that allows preload scripts to run in iframes. It's experimental, so we didn't try that route.

The postMessage handshake is pragmatic but departs from how extensions work in chrome.

We can start with the postMessage approach to get things working, and then explore more integrated injection in the future?

Extension ↔ runtime communication: WebSocket support

The RFC proposes inspectedWindow.eval() and Runtime.addBinding/Runtime.bindingCalled as the communication channel between extensions and the React Native runtime. Existing Expo devtools plugins communicate via websockets, so we hope to keep that working. CDP and websockets aren't mutually exclusive. However, the documented API would be CDP.

Extension descriptor and manifest

The main use for the manifest now would be its discovery by frameworks, is that so? Then it gets transformed into the extension descriptor shape that the dev middleware consumes.

Extension discovery in the frontend: embedder script vs JSON endpoint

Your draft serves extensionsConfig.js as a <script defer> that sets globalThis.__DEVTOOLS_EXTENSIONS__ before the frontend boots.

An alternative we can consider fetching a JSON endpoint, so DT frontend asks the dev middleware for a list of extensions. This avoids leaking the knowledge of __DEVTOOLS_EXTENSIONS__ from frontend to the dev middleware, and doesn't delay the devtools startup.

Happy to discuss this further!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants

Comments