Skip to main content

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

ModeDescription
"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:

PlatformManifest KeyPermission
Chrome / Edge / Brave / Operaside_panel.default_pathsidePanel
Firefoxsidebar_action.default_panel(none)
SafariNot 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:

  1. The side pane is in managed mode, and
  2. 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: true disables 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:

  1. 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).
  2. Defense-in-depth re-assertion — The generated bootstrap also calls setPanelBehavior inside both chrome.runtime.onInstalled and chrome.runtime.onStartup listeners, 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.

note

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.