Skip to content

Rozenite

Experimental — not yet verified against a real running app. Rozenite’s plugin API is young and still moving. This package was built and tested against Rozenite 1.13.0 (specifically the Rozenite monorepo commit 3d7ab5307da9256e183d3a8dfd4e9bca00ce2960, 2026-06-24, matching the 1.13.0 release published to npm for rozenite, @rozenite/vite-plugin, @rozenite/plugin-bridge, and @rozenite/metro). Its unit tests (bun run --cwd packages/hakka-rozenite test) cover the pure RN-side bridge logic, the panel store adapter, and the panel component with Rozenite’s client mocked — none of them exercise a real Rozenite messaging channel, a real Metro/Re.Pack plugin auto-discovery pass, or a real React Native DevTools session. Treat this package’s shape as likely to change alongside Rozenite’s, not a stable contract, and run the manual verification steps below yourself before relying on it.

hakka-rozenite renders Hakka’s network inspector as a panel inside React Native DevTools, via Rozenite. It renders the same hakka-browser/elements custom elements the rest of the Hakka web ecosystem uses — <hakka-request-list>, <hakka-request-detail>, <hakka-filter-bar> — through their typed hakka-browser/react wrappers, fed live from hakka-react-native’s capture stream.

Reach for this only if you already use React Native DevTools and have Rozenite configured in your app’s Metro/Re.Pack config. If you don’t, hakka-react-native’s own in-app inspector (covered in React Native Package) or the desktop bridge are the non-experimental ways to see RN network traffic.

Terminal window
bun add hakka-rozenite

hakka-react-native is a peer dependency, and it’s pinned to the exact version 0.1.0 (not a range) — a stricter pin than most peer dependencies in this ecosystem, worth noting if you’re used to range-based peers elsewhere.

Requires Rozenite already configured in your app’s Metro/Re.Pack config — see Rozenite’s own quick start; this package doesn’t configure that for you, the same way every other Rozenite plugin doesn’t.

Call the hook once, alongside useHakka():

import { useHakkaRozeniteDevTools } from 'hakka-rozenite'
function App() {
useHakka()
useHakkaRozeniteDevTools()
// ...
}

useHakkaRozeniteDevTools is exported from this package’s react-native entry (hakka-rozenite/hakka-rozenite/react-native — both resolve to the same build). It’s lazy-required and a no-op on web, under SSR, and in production bundles, so it’s safe to call unconditionally rather than gating it behind a dev-only check yourself.

hakka-rozenite/
├── rozenite.config.ts # one panel: src/ui/App.tsx
├── react-native.ts # lazy RN entry point (no-op in prod/web/SSR)
└── src/
├── shared/protocol.ts # plugin id + event map
├── react-native/bridge.ts # createHakkaRozeniteBridge — RN-side wiring
└── ui/
├── App.tsx # the panel: FilterBar + RequestList + RequestDetail
└── panelStore.ts # store adapter fed by client messages

RN side. useHakkaRozeniteDevTools() gets a Rozenite client (useRozeniteDevToolsClient from @rozenite/plugin-bridge), then createHakkaRozeniteBridge flushes Hakka.getLogs() as individual request frames, subscribes to Hakka.onRequest for live ones, and answers the panel’s get-snapshot/clear messages. It imports Hakka straight from hakka-core, the same thing hakka-react-native’s own HakkaBridge.ts does for its desktop-bridge integration.

Panel side. App.tsx gets its own Rozenite client, builds a panelStore (getSnapshot()/subscribe()/clear()/ingest()) that mirrors incoming request/cleared frames, and passes that store into both <FilterBar store> and <RequestList store> from hakka-browser/react — those two wire together automatically via hakka-browser/elements’s shared FilterViewModel/SelectionViewModel singletons. Selecting a row passes the resolved NetworkRequest object directly into <RequestDetail request>, bypassing hakka-browser/elements’s request-id-against-shared-store resolution entirely, since this plugin’s store is never that shared singleton.

Data path: Rozenite messaging, not the desktop bridge hub

Section titled “Data path: Rozenite messaging, not the desktop bridge hub”

Captured requests reach the panel over Rozenite’s own useRozeniteDevToolsClient messaging — not by connecting the panel directly to the Hakka desktop bridge hub (ws://localhost:8989, Bridge overview’s protocol). Every captured request is sent as its own client.send('request', ...) call — one frame per request, no batching, mirroring hakka-react-native’s own desktop-bridge behavior. There’s no separate pre-connect queue: Hakka.getLogs() is already hakka-react-native’s persistent capture store, so the bridge reads it directly (flushBacklog()) the moment the client connects.

This has not been load-tested against a real high-volume capture session. If a real app shows the Rozenite channel struggling under volume, the documented (but unimplemented) fallback is connecting the panel to the desktop bridge hub instead, reusing hakka-react-native’s existing HakkaBridge/desktop wire protocol.

This plugin is v0.1.0 and read-only: unlike Rozenite’s own official @rozenite/network-activity-plugin, it does not yet let you edit a response before it reaches the app. hakka-core’s mockEngine/ThrottleEngine — already used by hakka-react-native’s own Mocks/Throttle panels — are the natural next step for this panel, but they aren’t wired in here yet.

Testing — what’s covered, what needs a real app

Section titled “Testing — what’s covered, what needs a real app”

Covered (bun run --cwd packages/hakka-rozenite test, happy-dom + vitest):

  • react-native/bridge.test.ts — backlog-flush/live-forward/get-snapshot/clear wiring, against a fake Rozenite client and a fake Hakka facade.
  • ui/panelStore.test.ts — the store adapter’s upsert-by-id mirroring, subscriber fan-out, clear()/ingest()/destroy().
  • ui/App.test.tsx — mounts the real panel with useRozeniteDevToolsClient mocked; verifies the connecting state, that all three custom elements mount and register, row selection reaching the detail pane, and listener cleanup on unmount.

Not covered — needs a real app with React Native DevTools open:

  • useHakkaRozeniteDevTools() calling the real useRozeniteDevToolsClient — its channel depends on global.__FUSEBOX_REACT_DEVTOOLS_DISPATCHER__ (device) or a live postMessage-connected parent (panel), neither reproducible in a unit test.
  • Whether the RN ↔ panel round trip holds up under a real capture session’s volume.
  • Whether rozenite build’s Metro/Re.Pack auto-discovery actually picks this plugin up and mounts its panel in a real React Native DevTools sidebar.

Manual verification steps (do this before treating the integration as done):

  1. In a Rozenite-enabled RN app (or the Rozenite monorepo’s own playground app), link this package (and hakka-browser — for its /react and /elements subpaths — hakka-core/hakka-react-native alongside it).
  2. Call useHakkaRozeniteDevTools() once, alongside useHakka().
  3. Run with ROZENITE_DEV_MODE=hakka-rozenite npx react-native start (or expo start), per Rozenite’s own dev-mode docs, to load this plugin’s panel live instead of a built dist/.
  4. Open React Native DevTools and confirm a “Hakka” panel appears in the sidebar.
  5. Trigger some network requests; confirm they appear live in the panel’s request list, and that the detail pane and filter bar behave as expected.
  6. Click “Clear” in the panel; confirm Hakka.getLogCount() in the app goes to zero too, not just the panel’s own view.
  • “Load sample traffic” in the empty-state <RequestList> only updates the panel’s own local mirror. This plugin has no synthetic demo-data source, so ingest() on the store adapter never forwards anything back to the device.
  • No batching. Every request is its own message frame — see “Data path” above.
  • Read-only — no mock/throttle/breakpoint controls in the panel yet (see above).
  • Components — the custom elements this panel renders.
  • React — the typed wrappers this panel uses to render them.
  • React Native Packagehakka-react-native, the capture source this panel is fed from.
  • Bridge overview — the desktop bridge hub this panel deliberately does not use (see “Data path” above).