Rozenite: The Missing Plugin Layer for React Native DevTools
If you have built a React Native app in the last couple of years, you have probably felt the debugging tooling shift under your feet. Flipper used to be the standard, a separate desktop app full of plugins for network traffic, Redux state, storage browsers, and more. Then React Native 0.76 removed Flipper from the default template and replaced it with React Native DevTools, the Chrome-DevTools-based debugger that ships with the framework. The new debugger is genuinely nice for the console, breakpoints, and React profiling, but it has one glaring limitation: it is not extensible. There is no public way to add your own panel, so all those Flipper conveniences quietly disappeared.
Rozenite is the answer to that gap. Built by Callstack (the same React Native consultancy behind Re.Pack and Reassure), it is a plugin framework and plugin ecosystem layered on top of React Native DevTools. Install a plugin package, open the debugger, and a new panel appears automatically, scoped to the connected device. You can pull in official panels for network activity, MMKV, Redux, TanStack Query, SQLite, and navigation, or scaffold your own app-specific panels in TypeScript and ship them to your team over npm. The whole thing is named after a mineral, keeping with Callstack's rock-and-stone naming theme, and it slots into the tool you already use instead of asking you to babysit yet another desktop window.
Why Rozenite Earns a Spot in Your Toolbox
Rozenite is not just a single panel; it is an extension layer with a real ecosystem around it. Here is what makes it worth adopting.
- Plug-and-play official panels. Network inspection with request overrides, MMKV and SQLite browsers, Redux time-travel, TanStack Query cache inspection, React Navigation state, performance monitoring, and more, all as installable
@rozenite/*packages. - Custom panels in TypeScript and React. The
rozeniteCLI scaffolds a full plugin project so you can build feature-flag inspectors, domain-state viewers, or anything specific to your app. - A typed message bridge. Communication between the panel UI and your running app is isomorphic and type-safe, built on the Chrome DevTools Protocol and abstracted by
@rozenite/plugin-bridge. - Production safety by default. Rozenite is disabled unless you explicitly enable it, and plugins no-op or drop out of production builds automatically.
- AI-driven debugging. A newer agent stack lets an automation or AI agent drive a live app, listing network requests or inspecting state programmatically.
- Callstack-backed and actively maintained. MIT licensed, releasing every few weeks, and built by people deep in React Native core work.
Getting It Into Your Project
Rozenite supports npm, yarn, pnpm, and bun. The fastest path is the automatic setup, which detects your bundler, installs the right dependencies, and patches your config:
npx rozenite@latest init
# or: yarn dlx rozenite@latest init
# or: pnpm dlx rozenite@latest init
# or: bunx rozenite@latest init
If you prefer to wire things up by hand on a Metro project, install the Metro integration:
npm install -D @rozenite/metro
# or
yarn add -D @rozenite/metro
One thing to know up front: Rozenite expects React Native DevTools, which means React Native 0.76 or newer in practice (that is the release where the new debugger became the default). You will also want Node 20 or later.
Wiring Up the Bundler
Rozenite hooks into your build through a bundler plugin. For Metro, you wrap your config with withRozenite. Because Rozenite is disabled by default for safety, you typically gate it behind an environment variable so nothing leaks into production builds:
// metro.config.js
const { mergeConfig, getDefaultConfig } = require("@react-native/metro-config");
const { withRozenite } = require("@rozenite/metro");
const defaultConfig = getDefaultConfig(__dirname);
const customConfig = {};
module.exports = withRozenite(mergeConfig(defaultConfig, customConfig), {
enabled: process.env.WITH_ROZENITE === "true",
});
With that in place, the bundler auto-discovers any installed @rozenite/* plugins from node_modules, serves their panel UIs, and handles hot module reloading while you develop plugins. To run with Rozenite active, you start your bundler with the flag set:
WITH_ROZENITE=true npx react-native start
If you are on Re.Pack instead of Metro, the story is nearly identical; you import withRozenite from @rozenite/repack and wrap your config the same way. The withRozenite options also accept include and exclude arrays so you can control exactly which plugins load, which is handy when you only want a subset active in a given session.
Installing Your First Official Panel
Let's make a panel actually show up. The File System plugin is a good example because it demonstrates the adapter pattern several plugins use. Install the plugin plus one filesystem provider:
npm install @rozenite/file-system-plugin
# then pick exactly one provider:
npm install expo-file-system # for Expo apps
# or
npm install @dr.pogodin/react-native-fs # for bare React Native
Inside your app, call the plugin's hook near the root of your component tree and hand it an adapter built from your provider:
import * as FileSystem from "expo-file-system";
import {
createExpoFileSystemAdapter,
useFileSystemDevTools,
} from "@rozenite/file-system-plugin";
function App() {
useFileSystemDevTools({
adapter: createExpoFileSystemAdapter(FileSystem),
});
return <YourApp />;
}
Now open React Native DevTools and a File System panel appears. It lets you browse the document, cache, temp, library, and bundle directories, preview text and images inline (with a hex fallback for binary files), and, as of version 1.11, import and export files on demand. The same install-a-package-then-call-a-hook rhythm applies to the other official panels: @rozenite/redux-devtools-plugin for state and time travel, @rozenite/tanstack-query-plugin for your query cache, @rozenite/mmkv-plugin for key-value storage, and @rozenite/react-navigation-plugin for navigation state.
Inspecting Network Traffic Like a Web Developer
One of the most missed Flipper features was a proper network inspector, and @rozenite/network-activity-plugin brings it back with a browser-DevTools-style experience. After installing it, you get a real-time request inspector with headers, timing, status codes, searchable history, and a request timeline, all inside the same debugger window.
npm install @rozenite/network-activity-plugin
The standout capability here is response overriding. Instead of reaching for a separate proxy like Charles or Proxyman to mock a flaky endpoint, you can intercept and override responses right from the panel. The plugin is built on the Chrome DevTools Protocol, and it understands react-native-nitro-fetch traffic, labeling requests as built-in or Nitro so you can see exactly which transport handled each call. You can also export a session to share a reproduction with a teammate. For a lot of day-to-day debugging, this single panel replaces an entire second application.
Building a Custom Panel of Your Own
The real power of a plugin framework shows up when you build something specific to your app. Maybe you want a panel that lists your feature flags, or one that dumps a custom in-memory cache. The rozenite CLI scaffolds the whole project for you:
npx rozenite generate # scaffold a new plugin project
npx rozenite dev # vite dev server + RN watchers + HMR
npx rozenite build # build for production or to publish to npm
A plugin declares its panels in a rozenite.config.ts file. Each panel points at a source file that exports a React component:
// rozenite.config.ts
export default {
panels: [
{ name: "Feature Flags", source: "./src/feature-flags.tsx" },
],
};
The panel runs as a web UI inside DevTools, and it talks to your running app over the typed bridge from @rozenite/plugin-bridge. You define an event map describing the messages the two sides exchange, then use the useRozeniteDevToolsClient hook to send and receive them:
import { useEffect } from "react";
import { useRozeniteDevToolsClient } from "@rozenite/plugin-bridge";
type FeatureFlagEvents = {
"flags-updated": { flags: Record<string, boolean> };
"toggle-flag": { key: string; value: boolean };
};
function FeatureFlagsPanel() {
const client = useRozeniteDevToolsClient<FeatureFlagEvents>({
pluginId: "feature-flags",
});
useEffect(() => {
if (!client) return;
const sub = client.onMessage("flags-updated", (payload) => {
console.log("flags are now", payload.flags);
});
client.send("toggle-flag", { key: "new-checkout", value: true });
return () => sub.remove();
}, [client]);
return <div>Feature Flags</div>;
}
Because the event map is shared between the panel and the app-side hook, every message is type-checked end to end. When you are happy with the panel, rozenite build produces something you can publish to npm, and from then on any teammate who installs your package gets the panel automatically the next time they open DevTools.
Letting an Agent Drive the App
A more forward-looking corner of Rozenite is its agent stack: the rozenite agent CLI alongside @rozenite/agent-sdk and @rozenite/agent-bridge. The idea is to let an AI agent or any automation script inspect and drive a live app. The app exposes "domains" (like a built-in network domain), each offering a set of "tools" that can be discovered and called programmatically.
import { createAgentClient } from "@rozenite/agent-sdk";
const client = createAgentClient();
const result = await client.withSession(async (session) => {
const domains = await session.domains.list();
const tools = await session.tools.list({ domain: "network" });
const requests = await session.tools.call({
domain: "network",
tool: "listRequests",
args: { limit: 20 },
autoPaginate: { pagesLimit: 2 },
});
return { domains, tools, requests };
});
On the app side, @rozenite/agent-bridge gives you a React hook for registering your own agent tools, so an agent could query your custom domain state the same way it queries network traffic. The Network Activity plugin even doubles as a fallback network domain on older React Native versions where the built-in domain is not available. This positions Rozenite as a bridge for AI-assisted debugging, where an agent can ask "show me the last twenty failed requests" and get a structured answer from a real running app.
A Few Things to Keep in Mind
Rozenite is young but solid. The 1.0 release landed in November 2025 after a long alpha, and the API still moves fairly quickly, with the agent stack being a recent addition, so pin your versions and keep all @rozenite/* packages in lockstep since they share the framework version. Only Metro and Re.Pack are first-class bundlers; there is no generic Webpack path. And remember that nothing appears unless you actually enable it, which is the single most common first-run surprise, so double-check that WITH_ROZENITE=true is set when a panel seems to be missing. Individual plugins also carry their own peer requirements, like the File System plugin needing exactly one provider adapter.
The Takeaway
Rozenite is essentially the extensibility that Flipper had, rebuilt cleanly on top of the official, modern React Native DevTools. If you adopted the new debugger after 0.76 and found yourself missing your network inspector, your Redux panel, or a storage browser, Rozenite brings all of that back into the single window you already open, without a heavyweight second app to maintain. The plug-and-play official plugins cover the common cases in minutes, the custom plugin SDK lets you ship app-specific tooling to your whole team, and the agent stack hints at a future where AI helpers can poke at a live app on their own. For a young, Callstack-backed, MIT-licensed project, that is a remarkably complete debugging story, and it is well worth a place in any modern React Native workflow.