One has two development tool surfaces:
Both work in development with no setup. You can add your own panels to the web overlay, and Rozenite plugin packages automatically appear in React Native DevTools.
For the web overlay, press Alt+Space (Option+Space on Mac) to open the spotlight menu, then select a tool. You can also use keyboard shortcuts to jump directly to a specific panel:
Press Escape to close any panel.
For React Native, press j or dt in the one dev terminal. That opens React Native DevTools for the connected Hermes app, with React DevTools, the JavaScript console, sources, network, and memory panels.
React Native DevTools attaches to one app at a time. If another debugger or a CDP client is attached, close it before opening DevTools again.
See how your page appears in Google search results and check for common SEO issues:
Debug the current route state:
[id])Visualize loader performance with a waterfall breakdown:
Useful for identifying slow loaders and optimizing data fetching.
Track errors from error boundaries and loaders:
Errors are collected in real-time so you can see issues as they happen.
The inspector is injected automatically in development when devtools.inspector is enabled (the default). It works through the data-one-source attribute One adds to components during development. Hold Option (Mac) or Alt (Windows/Linux) for 0.8 seconds and hover over any element to see its source location highlighted. Click to open a picker showing:
Files open in the editor configured via devtools.editor, or the LAUNCH_EDITOR / EDITOR environment variable.
one also exports a SourceInspector component with its props type, kept for backwards compatibility:
import { SourceInspector } from 'one'
// deprecated: the inspector above is injected automatically.// this component renders nothing. remove it for cleaner code.export function Layout() { return <SourceInspector />}SourceInspectorProps accepts hotkey and enabled, but both are deprecated: the activation key and enabled state now come from the injected devtools script and the devtools.inspector config.
Record user interactions for bug reports or AI-assisted debugging. Start recording from:
After a 2-second countdown, recording begins. Tap Option to stop and copy the log to clipboard.
The output is a compact, line-based format optimized for AI consumption:
# https://localhost:3000/dashboard# components: Button < Card < Dashboard# selector: [data-testid="submit-btn"]# window: 1920x1080 scroll:0,0# duration: 3200ms events: 28
0 click 450,320 btn:0 [#submit] @Button.tsx:15 "Submit"85 keydown Tab142 focus [input.email]200 input val:"test@" [input.email]
# final hover: [.sidebar > nav]# final source: Nav.tsx:42:5Events captured include clicks, keyboard input, scroll, focus/blur, input changes, wheel, resize, and more.
The dev tools panel is:
A plugin is a module that default exports a OneDevtoolsPlugin. Use the defineDevtoolsPlugin helper so the editor checks the shape:
import { defineDevtoolsPlugin } from 'one/devtools'
export default defineDevtoolsPlugin({ name: 'runtime-inspector', title: 'Runtime Inspector', category: 'Tuning', // An iframe keeps a devtool that already runs its own UI and server as-is. panel: { url: 'http://localhost:4577', }, actions: [ { id: 'open-web-panel', title: 'Open Runtime Inspector', run() { window.open('http://localhost:4577', '_blank') }, }, ],})Register it in vite.config.ts:
import { one } from 'one/vite'
export default { plugins: [ one({ devtools: { plugins: ['./devtools/runtime-inspector.ts'], }, }), ],}Plugin modules run in the browser. They can add a panel, actions, or both:
panel.url loads an existing devtools page in an iframe. Use this for tools that already run their own frontend and server.panel.mount(root, context) renders a panel directly into the overlay. Return a cleanup function to remove listeners when the panel closes.actions appear in the Alt+Space spotlight and receive the same context as panels.The context exposes project, url, openPanel, and closePanel. Panel ids are namespaced as plugin:<name>. Two plugins cannot claim the same name.
Plugins are loaded only in development. The module is resolved through Vite, so local TypeScript and JSX files work without a build step.
One hosts the Rozenite plugin convention for React Native. If a Rozenite plugin package is installed in your app, One mounts its plugin host on the native dev server and React Native DevTools shows the panels. Existing Rozenite plugins work without a Metro wrapper or a separate withRozenite config.
bun add -d @rozenite/storage-pluginOn the next one dev, pressing j or dt opens DevTools with the Storage panel. One logs the plugin names it found:
[one] React Native DevTools plugins: @rozenite/storage-plugin (open with dt or j)Discovery checks direct dependencies for a Rozenite manifest (dist/rozenite.json), the same convention Rozenite’s Metro integration uses. To pin the list or exclude a plugin:
one({ devtools: { rozenite: { include: ['@rozenite/storage-plugin'], exclude: ['@rozenite/network-activity-plugin'], pluginDisplay: 'sidebar', // or 'tabs' }, },})Set rozenite: false to keep the stock React Native DevTools without the Rozenite host. When no Rozenite plugin is installed, One leaves the debugger frontend untouched.
One hosts Rozenite when One’s Vite bundler serves the native app, which is the default. If you set native.bundler: 'metro', keep using Rozenite’s own withRozenite Metro wrapper instead.
The device half of a Rozenite plugin still lives in your app. Follow the plugin’s install instructions and import it once from your app entry when it asks you to.
Runtime Inspector is the reference adapter for this API. It already ships its semantics and layouts as a protocol-first package with a Rozenite panel. In a One app:
@runtime-inspector/react-native and @runtime-inspector/panel-rozenite to the app.import '@runtime-inspector/panel-rozenite'.Its web panel already runs as its own server, so the web adapter is a plugin with panel: { url } like the example above. No Runtime Inspector protocol change is needed for either host.
Dev tools are enabled by default in development mode. To disable them, set:
import { one } from 'one/vite'
export default { plugins: [ one({ devtools: false, }), ],}To keep the web tools but change individual features:
one({ devtools: { inspector: true, editor: 'cursor', seoPreview: true, // browser plugin modules for the One overlay plugins: ['./devtools/my-panel.ts'], // Rozenite host for React Native DevTools rozenite: { pluginDisplay: 'tabs', }, },})Edit this page on GitHub.