Skip to main content

Custom Panels

A custom panel replaces the entire content area of the UI (everything except the sidebar) with your own custom element, and gets its own item in the sidebar. This page documents the frontend side of the contract: how the frontend loads your code, which element it instantiates, and which properties it keeps in sync. Registering the panel itself happens on the core side and is covered by the backend documentation.

What the frontend receives

For a custom panel route, the panel info carries a _panel_custom configuration object:

interface CustomPanelConfig {
name: string;
embed_iframe: boolean;
trust_external: boolean;
js_url?: string;
module_url?: string;
html_url?: string;
}

Source selection and loading

  • module_url is loaded as an ES module (recommended).
  • js_url is loaded as a classic script; each URL is only imported once per session.
  • If both are provided, the modern browser build loads module_url and the legacy build loads js_url.

Your script must define a custom element. The frontend then creates:

  • an element named exactly config.name — e.g. name: my-panel means customElements.define("my-panel", ...); or
  • for legacy html_url panels, an element named ha-panel-${name}.

External URL confirmation

If the panel URL's hostname is not localhost, 127.0.0.1, or the host currently serving the UI, and trust_external is not set, the user is asked to confirm before the code is loaded. Setting trust_external: true in the panel registration suppresses the prompt.

Properties your element receives

Once created, the panel element is kept up to date with these properties:

PropertyDescription
apexThe application state object (states, connection, user, service helpers). Reassigned on every state change.
narrowBoolean; true when the UI is in the narrow (mobile) layout.
routeThe current route object, so your panel can implement its own sub-routing.
panelThe panel info object, including your _panel_custom config (useful for passing custom config to the panel).

If your element defines a setProperties(props) method, the frontend calls it with the changed properties in a single batch; otherwise each property is assigned directly.

Iframe embedding

With embed_iframe: true, the panel runs inside a sandboxing <iframe> instead of the main document. The frontend writes a bootstrap document into the iframe and exposes the host element as window.customPanel for the bridge script. The same properties (apex, narrow, route, plus the panel config) are forwarded across the iframe boundary. Because navigation events fire on the main window, the host element exposes a navigate(path, options) function that embedded panels use to change the app's location from inside the iframe.

Styling

The panel container's background is set to var(--primary-background-color), and your element renders inside the normal theme scope, so all theme variables are available.