DevTools
HexaJS DevTools runs as a managed React surface. The managed UI bootstrap wires DI for you, so React code can resolve services and tokens directly.
Enable managed React devtools
{
"ui": {
"devtools": {
"mode": "managed",
"sourceDir": "ui/devtools",
"indexFile": "index.html"
}
}
}
mode: "managed" runs the internal managed React build for DevTools.
parallelBuild defaults to true and runs managed popup + devtools builds in parallel during standard builds.
Set parallelBuild to false to force sequential managed UI builds.
Resolve DevTools services with DI
import { inject } from '@hexajs-dev/common';
import { RuntimePort } from '@hexajs-dev/ports';
import { HexaUIClient } from '@hexajs-dev/ui';
const runtimePort = inject(RuntimePort);
const hexaUIClient = inject(HexaUIClient);
Use HexaUIClient for typed request/response calls and RuntimePort for DevTools-side runtime events.
Request data from background
const [configResponse, clipsResponse] = await Promise.all([
hexaUIClient.sendMessage<GetConfigMessage, ConfigResponseMessage>(configApi.Get, new GetConfigMessage(Date.now())),
hexaUIClient.sendMessage<GetClipsMessage, ClipsResponseMessage>(clipboardApi.Get, new GetClipsMessage(Date.now()))
]);
Resolve platform token
import { HEXA_PLATFORM, inject } from '@hexajs-dev/common';
const platform = inject(HEXA_PLATFORM);
For constructor-based DI, token values use @Inject(...):
import { Inject, Injectable, HexaContext, HEXA_PLATFORM } from '@hexajs-dev/common';
@Injectable({ context: HexaContext.UI })
export class PlatformTokenExample {
constructor(@Inject(HEXA_PLATFORM) readonly platform: string) {}
}
Customize the Vite build
Each managed surface ships with a vite.config.ts in its source directory. HexaJS merges it with the internal build config, so you can extend it without replacing defaults.
import { defineConfig } from 'vite';
import path from 'path';
export default defineConfig({
resolve: {
alias: {
'@': path.resolve(__dirname, './src'),
},
},
css: {
modules: {
localsConvention: 'camelCase',
},
},
define: {
'import.meta.env.EXTENSION_VERSION': JSON.stringify(process.env.npm_package_version),
},
plugins: [
// add extra Vite plugins here
],
});
Merge behaviour:
plugins— your plugins are loaded first, then HexaJS injects its own (React, bootstrap). Duplicates by name are skipped.resolve.alias— merged with path aliases derived fromtsconfig.jsonin the source directory.define— merged with HexaJS platform defines.build— you can extend most options, but managed output controls are preserved:outDir,emptyOutDir,rollupOptions.input, androllupOptions.output. The internalexternallist is always preserved and merged with your additions.
The React framework plugin (@vitejs/plugin-react) is injected automatically. You do not need to add it — if it is detected in your config it will be skipped to avoid loading it twice.
Notes
- Keep long-lived state in background/content stores, not in DevTools surface state.
- Use DI directly in React code (
inject(...)) for UI services/ports. - Token injection in HexaJS uses
inject(TOKEN)or@Inject(TOKEN).
Classes
HexaUIClient
UI-context HexaClient. Sends messages from popup/devtools UI to the background.
import { HexaUIClient } from '@hexajs-dev/ui';
class HexaUIClient { ... }
Methods
sendMessage()
Send a message and await a response. Content → background uses runtime.sendMessage. Background → content requires a tabId — use BackgroundHexaClient.sendToTab().
sendMessage<TPayload, TResponse>(target: `${namespace}:${api}`, payload?: TPayload): Promise<TResponse>