opentui

Build terminal UIs with OpenTUI. Covers Core, frameworks, components, application APIs, testing, extensions, integrations, deployment, and public API lookup.

Install
npx skills add 'https://github.com/anomalyco/opentui/tree/main/packages/web/src/content'
Download bundle ↓
main · ac753b4Scanned 2026-09-15

Contributors

GitHub-linked commit authors for this SKILL.md at the saved revision. Co-authors and history before file renames are not included.

File history ↗

docs/plugins/solid.mdx

docs/plugins/solid.mdxBrowse 89 files
View on GitHub
← Back to SKILL.md

title: Solid plugin slots description: Solid slot components backed by the shared plugin registry

Solid plugin slots

This page shows Solid integration for plugin slots.

Use Solid slots when registered plugins must return JSX.Element values for host-defined regions. The host owns the layout and slot types. Plugins receive only the context and props that the host supplies.

Start with Plugin slots for the shared registry, mode, ordering, and error model.

What Solid adds

  • createSolidSlotRegistry(renderer, context, options?): Creates a registry for JSX.Element values.
  • Slot<TSlots, TContext>: Renders a slot from the required registry prop.
  • createSlot(registry, options?): Returns a registry-bound <Slot /> component.
  • SolidPlugin<TSlots, TContext>: Describes a plugin that contributes JSX.Element values.

createSolidSlotRegistry accepts the shared SlotRegistryOptions. Register plugins directly with registry.register(). Unlike Core, Solid does not need a registration wrapper or managed renderable ownership hooks.

Solid hosts use @opentui/solid/runtime-plugin-support when contributions come from Bun runtime-loaded TSX modules. See Load plugins and modules at runtime for setup and module maps.

Basic usage

import { createCliRenderer } from "@opentui/core"
import { createSolidSlotRegistry, Slot, render } from "@opentui/solid"

type Slots = {
  statusbar: { user: string }
}

const context = { appName: "solid-app", version: "1.0.0" }
const renderer = await createCliRenderer()

const registry = createSolidSlotRegistry<Slots, typeof context>(renderer, context)

const unregister = registry.register({
  id: "clock-plugin",
  slots: {
    statusbar(ctx, props) {
      return <text>{`${ctx.appName}:${props.user}`}</text>
    },
  },
})

const AppSlot = Slot<Slots, typeof context>

const App = () => (
  <AppSlot registry={registry} name="statusbar" user="sam" mode="replace">
    <text>fallback-statusbar</text>
  </AppSlot>
)

render(() => <App />, renderer)

Optional convenience helper

Bind a registry once when you do not want to pass it to each slot:

const AppSlot = createSlot(registry)

<Slot> props

PropTypeRequiredDescription
registrySlotRegistry<JSX.Element, Slots, Context>yesRegistry to resolve plugins from
namekeyof SlotsyesWhich slot to render
modeSlotModeno"append" (default), "replace", or "single_winner". See slot modes.
pluginFailurePlaceholder(failure: PluginErrorEvent) => JSX.ElementnoPer-slot placeholder UI when a plugin throws
childrenJSX.ElementnoFallback UI
remainingSlots[name]variesSlot-specific props forwarded to plugin renderers

SolidSlotOptions (for createSlot)

OptionTypeRequiredDescription
pluginFailurePlaceholder(failure: PluginErrorEvent) => JSX.ElementnoCreates placeholder UI when a plugin throws

Plugin failure placeholders

const Slot = createSlot(registry, {
  pluginFailurePlaceholder(failure) {
    return <text>{`plugin-error:${failure.pluginId}:${failure.phase}`}</text>
  },
})

If a contribution throws, the slot renders the placeholder. In single_winner mode, the slot uses children if no placeholder exists or the placeholder returns null.

In replace mode, an initial contribution failure without a usable placeholder adds no output. If every initial contribution fails this way, the slot uses children.

A later subtree failure uses children in single_winner mode and in replace mode with one contribution. In replace mode with multiple contributions, only the failed subtree disappears.

The slot catches a failure from the initial contribution call directly. An internal Solid <ErrorBoundary> catches failures from the reactive subtree. If the placeholder throws, the registry reports an error_placeholder failure and treats the placeholder as unavailable.

Lifecycle and disposal

The <Slot> component subscribes to registry changes in its Solid owner. Owner cleanup removes that subscription and disposes contribution subtrees. Solid owns the lifecycle of the returned JSX.Element values.

When a component registers a plugin, register its cleanup with onCleanup:

import { onCleanup } from "solid-js"

const unregister = registry.register({
  id: "clock-plugin",
  slots: {
    statusbar: () => <text>clock</text>,
  },
})

onCleanup(unregister)

Unregistration calls the plugin's dispose hook. Renderer destruction disposes the Solid root, clears the registry, and calls dispose. Unlike Core managed contributions, Solid contributions do not receive onActivate, onDeactivate, or onDispose node hooks. Unlike React, Solid uses owner cleanup rather than effect cleanup.

Example

See the Solid plugin slots example.