Skip to main content

Solid Integration

Managed UI SolidJS components run inside the same generated UI DI container that React, Vue, and Svelte components use. The framework adapter is selected via a single project-wide flag in hexa-cli.config.json:

{
"ui": {
"framework": "solid",
"popup": { "mode": "managed", "sourceDir": "ui/popup", "indexFile": "index.html" }
}
}

When ui.framework: "solid" is set, HexaJS injects vite-plugin-solid into the popup/devtools/newtab build pipelines and emits content @View overlays that mount Solid components inside shadow DOM via SolidShadowRenderer from @hexajs-dev/ui/solid.

Solid support requires solid-js@^1.8 and vite-plugin-solid@^2.9. The framework choice is project-wide; mixing Solid with React, Vue, or Svelte per surface is not supported.

Request data in onMount

import { onMount, createSignal } from 'solid-js';
import { inject } from '@hexajs-dev/common';
import { HexaUIClient } from '@hexajs-dev/ui';
import { configApi } from './api';
import type { ConfigResponseMessage, GetConfigMessage } from './messages';

export default function ConfigPanel() {
const [config, setConfig] = createSignal<unknown | null>(null);

onMount(async () => {
const hexaUIClient = inject(HexaUIClient);
const response = await hexaUIClient.sendMessage<GetConfigMessage, ConfigResponseMessage>(
configApi.Get,
new GetConfigMessage(Date.now())
);

if (response && !hasHexaError(response) && response.config) {
setConfig(response.config);
}
});

return <pre>{config() ? JSON.stringify(config(), null, 2) : 'Loading...'}</pre>;
}

inject(...) here is the HexaJS DI helper from @hexajs-dev/common. Solid's createSignal is the reactive primitive — equivalent to Vue's ref or Svelte's $state.

Send changes from an event handler

import { inject } from '@hexajs-dev/common';
import { HexaUIClient } from '@hexajs-dev/ui';
import { configApi } from './api';
import type { ConfigResponseMessage, UpdateConfigMessage } from './messages';

async function setTheme(nextTheme: 'light' | 'dark'): Promise<void> {
const hexaUIClient = inject(HexaUIClient);
await hexaUIClient.sendMessage<UpdateConfigMessage, ConfigResponseMessage>(
configApi.Update,
new UpdateConfigMessage({ theme: nextTheme })
);
}

export default function ThemeToggle() {
return <button onClick={() => setTheme('dark')}>Dark mode</button>;
}

Resolve token values

import { HEXA_PLATFORM, inject } from '@hexajs-dev/common';

export default function PlatformBadge() {
const platform = inject(HEXA_PLATFORM);
return <span>{platform}</span>;
}

Shadow @View overlays

Solid components mounted inside a shadow DOM via @View receive the controller instance through the controller prop, exactly matching the React, Vue, and Svelte renderer contracts:

// sample-overlay.view.ts
import { HexaView, View } from '@hexajs-dev/core';
import SampleOverlayComponent from './sample-overlay.component';
import styles from './sample-overlay.css?inline';

@View({ id: 'sample', component: SampleOverlayComponent, styles, anchorSelector: 'body' })
export class SampleOverlayView extends HexaView {
count = 0;
increment = (): number => ++this.count;
}
// sample-overlay.component.tsx
import { createSignal } from 'solid-js';
import type { SampleOverlayView } from './sample-overlay.view';

export default function SampleOverlay(props: { controller: SampleOverlayView }) {
const [count, setCount] = createSignal(props.controller.count);

function tick() {
setCount(props.controller.increment());
}

return <button type="button" onClick={tick}>Count: {count()}</button>;
}

CSS isolation works identically to React — import your styles with ?inline and pass them as styles to the @View decorator. No special plugin options are needed since Solid does not auto-inject component CSS into <head>.

Important scope reminder

Managed UI Solid components resolve UI/general services and tokens. They do not resolve HexaBackgroundStore/HexaContentStore directly; ask the background through HexaUIClient messages and let state live in the appropriate context.

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>