Build Your Own Hub UI
A hub UI provider implements two contracts, the node-side ui slot and the browser-side context. @devframes/hub-ui is the reference.
The node seam: DevframeHubUi
initHub({ ui }) takes pure data (see the ui
slot):
interface DevframeHubUi {
viewer?: { distDir: string } // a standalone SPA served at the hub base
embedded?: { entry: string } // a self-contained bootstrap at <base>embedded.js
assets?: Record<string, () => string | Uint8Array> // extra UI-owned files
setup?: (ctx) => void | Promise<void> // publish static config via ctx.staticConfig
}Ship a function returning this object (createUi()) with prebuilt assets using
relative paths.
setup(ctx) runs once at hub init: config in ctx.staticConfig is serialized
into ConnectionMeta.configs and read by the RPC client at handshake.
The browser-side contracts
A hub UI provider renders from the hub's shared state via
createDevframeClientRuntime(), which assembles the
DevframeClientContext (docks, commands, renderers, when-clauses, connection)
and loads dock client scripts. Honor:
Dock entry types
Render the built-in variants of the open dock union
(DevframeDockEntryRegistry, @devframes/hub/types): iframe (the entry's
url in a kept-alive iframe, honoring subTabs soft nav), action (a
dock-rail button running its client script), custom-render (a container its
client script mounts into), launcher (a launch call-to-action reflecting
launcher.status), group (one dock-rail button collapsing its members), and
~builtin (your native views for reserved ids). What to render per type is in
the Hub API reference.
Honor when / visibility, category grouping (order from
DEFAULT_CATEGORIES_ORDER, @devframes/hub/constants), and the
hub:docks:activate broadcast.
An iframe entry serving a remote assets package can
report it unreachable: its fallback page posts a RemoteAssetsErrorMessage
(DEVFRAME_REMOTE_ASSETS_ERROR_MESSAGE_TYPE, @devframes/hub/constants) to
window.parent. Match it against the iframe's contentWindow to offer install + retry.
The renderer registry and its fallback
Every other dock type routes through the dock-renderer registry: build it
with createDockRenderersContext() (@devframes/hub/client), wiring local
registrations and the hub's renderer manifest:
import { createDockRenderersContext } from '@devframes/hub/client'
const renderers = createDockRenderersContext({
context: () => context,
manifest: () => manifestState.value(), // the devframe:dock-renderers slot
})
const result = await renderers.mount(entry, container)Show a state per mount-result variant:
{ status: 'mounted', dispose }: the renderer owns the container; calldisposeon unmount.{ status: 'missing-renderer' }: render a fallback (renderers.has(type)answers up front).{ status: 'load-error', error }: import failed or the renderer threw; render the error with retry.
The theme contract for renderers
Renderer modules self-style (sometimes via a shadow root). Keep a live
dark class on the mount container and let CSS custom properties inherit; a
--devframe-primary ancestor rebrands rendered content.
Reference points
packages/hub-ui: the full reference hub UI provider (Vue,@antfu/design).examples/custom-hub-viteandexamples/custom-hub-next: hand-rolled hub UI providers in vanilla DOM and React.
Build Your Own JSON-Render Frontend
@devframes/json-render-ui is the reference frontend, not the protocol; any implementation of the renderer contract replaces it. The Next hub witness ships a React one at src/client/json-render/.
Built with Devframe
Real-world devtools and hub UI providers built on devframe, from Vite DevTools to ESLint Config Inspector.