Side Pane
The side pane is a UI surface that opens alongside the current page — similar to a sidebar — and is ideal for persistent tooling, reference panels, or contextual dashboards. HexaJS manages the side pane build, manifest entries, and HMR just like popup, devtools, and newtab.
Modes
| Mode | Description |
|---|---|
"managed" | Hexa builds and HMR-reloads your side pane source. Supports React, Vue 3, Svelte 5, and SolidJS. |
"external" | Use your own build pipeline. Hexa copies your built assets into the extension output. |
"none" | Side pane disabled (default). |
Configuration
Enable the side pane in hexa-cli.config.json:
{ "ui": { "framework": "react", "sidepane": { "mode": "managed" } } }
All four supported frameworks work identically — set ui.framework project-wide and write your side pane component in that framework.
Per-Browser Manifest Mapping
Hexa emits the correct manifest keys and permissions for each target platform automatically:
| Platform | Manifest Key | Permission |
|---|---|---|
| Chrome / Edge / Brave / Opera | side_panel.default_path | sidePanel |
| Firefox | sidebar_action.default_panel | (none) |
| Safari | Not supported | — |
When building for Safari, the side pane surface is silently skipped and no manifest entries are emitted.
Open-on-Click Behavior
On Chromium-based browsers, extensions can open the side panel when the user clicks the toolbar icon. HexaJS automatically emits the sidePanel.setPanelBehavior({ openPanelOnActionClick: true }) call in the generated background bootstrap when:
- The side pane is in managed mode, and
- No popup surface is configured (popup mode is
"none").
This is feature-detected at runtime — on Firefox and Safari the call is a no-op.
Manual open-on-click
If you use external mode, or your project has both a popup and a side pane, you can opt into open-on-click manually in your background code:
import { SidePanelPort } from '@hexajs-dev/ports';
@Controller()
export class SidePaneController {
constructor(private readonly sidePanel: SidePanelPort) {}
@OnInit()
async init() {
await this.sidePanel.setPanelBehavior({ openPanelOnActionClick: true });
}
}
Note: When both popup and side pane are active,
openPanelOnActionClick: truedisables the popup on action click. Choose one or implement your own toggle logic.
Framework Parity
Side pane supports all four managed UI frameworks identically:
- React (
@hexajs-dev/ui/react) - Vue 3 (
@hexajs-dev/ui/vue) - Svelte 5 (
@hexajs-dev/ui/svelte) - SolidJS (
@hexajs-dev/ui/solid)
The same DI container, token injection, and HexaUIClient messaging patterns from popup/devtools/newtab apply to the side pane surface.
Adding a Side Pane to an Existing Project
hexa add ui sidepane
This scaffolds the side pane source files using your project's configured ui.framework and updates hexa-cli.config.json.
If a popup is already configured, hexa add ui sidepane prints an informational note (the toolbar click keeps opening the popup; the side panel must be opened programmatically) and proceeds without asking for confirmation, since neither surface is modified or removed. The reverse direction — hexa add ui popup when a side pane already exists — asks for confirmation first, since it changes what the existing toolbar click does. See CLI Commands → add ui for details.
Watch / Auto-Launch Mode
When running hexa build --watch with auto-launch enabled, branded Chrome 137+ loads the extension via CDP (Extensions.loadUnpacked) rather than the traditional --load-extension flag. This registers the service worker but does not start it — meaning sidePanel.setPanelBehavior never executes and the toolbar icon appears unresponsive.
HexaJS handles this automatically:
- Service worker wake — After the extension is loaded via the debug pipe, the CLI sends CDP commands to discover, attach to, and evaluate a no-op on the extension's service worker target. This forces Chrome to start the worker and run its top-level module code (including
setPanelBehavior). - Defense-in-depth re-assertion — The generated bootstrap also calls
setPanelBehaviorinside bothchrome.runtime.onInstalledandchrome.runtime.onStartuplisteners, covering cold starts and worker restarts after suspension.
No changes are needed in your project code — the fix is entirely in the CLI build tooling and auto-launch path.
This applies only to branded Chrome 137+. Edge, Brave, and Opera still use --load-extension, which starts the service worker immediately. Firefox uses its own extension install flow.