Skip to main content

Plugin UI Kit & Styling

Use Daintree's host-served plugin UI kit, semantic Tailwind utilities, icons, panel controls, cleanup and performance hooks. Inspect Performance and Styles and run source lint.

Updated
Reviewed

Build plugin views with Daintree's controls, then use themed utilities for the layout around them. Your panel follows the user's theme and keeps the same keyboard behavior as the rest of the app.

Use the host UI kit

@daintreehq/plugin-ui is served by the running app through its import map. It is not an npm package. A raw JavaScript view can import it directly, alongside React and the host's @daintreehq/plugin-sdk/react hooks. A bundled view needs the current source version of @daintreehq/plugin-vite, which leaves the kit external.

Note
The npm 0.1.0 SDK and Vite preset predate the kit's type declarations, newer hooks, and preset externalization rule. Build the current source packages when you need those contracts. See the authoring toolchain.
import React from "react";
import { Button, PaneHeader } from "@daintreehq/plugin-ui";

export default function Overview() {
  return React.createElement("div", { className: "flex flex-col gap-3 p-4" },
    React.createElement(PaneHeader, { title: "Release notes" }),
    React.createElement(Button, { onClick: () => refreshNotes() }, "Refresh")
  );
}

This illustrates a raw view. Supply the data and refreshNotes() implementation through your worker channels. The kit supplies controls, not your project's behavior.

NeedKit examples
Controls and formsButton, Input, Select, Switch, FormField
Lists and dataDataTable, FileTree, VirtualList, LogView
Pane layout and statePaneHeader, PaneLayout, EmptyState, PaneState, Skeleton
SettingsSettingsSection, SettingsGroup, SettingsRow, SettingsActions
Overlays and documentsDialog, ConfirmDialog, DropdownMenu, Tooltip, Markdown

The type declarations give each component's props. The full kit reference covers the available exports. Add @daintreehq/plugin-sdk/plugin-ui and @daintreehq/plugin-sdk/view-globals to TypeScript's compilerOptions.types when using the source SDK.

Style with the host theme

Daintree compiles Tailwind utilities used by plugin views at runtime. You do not need a Tailwind build or preflight stylesheet. Use semantic classes such as bg-surface-panel, text-text-primary, and border-border-default. Their colors resolve to the current theme.

Layout, spacing, flexbox, grid, state variants, and container queries are available. Stock palette classes such as bg-red-500 produce no CSS. dark:, prose, and @apply are outside this styling contract. Use the kit's Markdown for a rendered document.

Keep conditional class names as complete strings. Put @container on an ancestor and container variants on its descendants, so the layout responds to the panel's width. A window breakpoint can leave a narrow pane using a wide layout.

The generated rules apply inside the host's marked view root. Spread styleRootAttributes from the view props onto a container you portal outside that root. Scope your own CSS to your plugin and place component rules in @layer components so utilities can override them. See the runtime styling implementation and complete vocabulary.

Icons and panel controls

Use the kit's Icon with a Lucide icon name for view content. Manifest icons can use a built-in id or a relative SVG asset. Daintree validates custom SVGs before rendering them; use the icon contract rather than embedding arbitrary SVG markup in a manifest field.

Declare up to three header buttons in a panel's toolbar and up to five entries in its menu. They must name your own actions. A view can update toolbar state and publish contextual menu entries with usePanelToolbarItem and usePanelMenuItems. The worker can publish entries with host.setPanelMenuItems, including for PTY panels that have no React view. See panel menus, toolbars, and reload.

Keep views responsive

Pull initial data when the view mounts, then subscribe to changes. Use useCachedHostChannel to share a read and revalidate after an invalidation burst, useSyncedCollection for keyed deltas, and useStreamBuffer for appended output. Select only the slice a component needs with usePluginEventSelector or useHostStore. For large lists, use useVirtualList or the kit's VirtualList.

Tie view-owned subscriptions, timers, observers, and requests to disposeSignal. The source SDK's createViewScope groups their cleanup. A view can unmount while its panel still exists, so durable jobs belong in the worker and end when the panel reports removed. See which signal to use.

Select the plugin in the Plugin Manager to inspect Performance once the host has measured activity. It shows activation, view load and paint, invokes, pushes, worker memory, and long frames associated with plugin activity, alongside budgets. These measurements describe cost; a budget overrun does not disable the plugin or prove it caused a long frame.

Styles is available for an enabled plugin with a rendered panel kind and matching view. Open a panel first, then use Check again to list classes that produced no host utility CSS. A class styled by your own CSS can appear in this list too. Project plugins offer the same sections in Project settings > Plugins, with Styles appearing after a view has loaded.

The current source CLI adds daintree-plugin lint [dir], with --json for structured results and --strict to fail on warnings. It checks source patterns and styling, including unsupported utility classes. It is not an execution test, and npm 0.1.0 does not include it. Pair it with a real panel check in Daintree.

Use Styles to find classes the host does not generate

Implementation references: kit facade, view hooks, measurements and Styles, and source lint, pinned to the October 13, 2026 audit.