Docs/API Reference/Plugins

Widgets and extension points

@veap/plugins renders plugin-contributed UI through two mechanisms: extension points (a named slot that a plugin targets with a component) and widget areas (a dashboard-style…

4 min read

@veap/plugins renders plugin-contributed UI through two mechanisms: extension points (a named slot that a plugin targets with a component) and widget areas (a dashboard-style region whose widgets a user can arrange). Both are declared by plugins and resolved by the PluginRegistry, filtered by the current user's roles and permissions.

Declaring contributions#

Plugins register their UI on the IPlugin definition (see the plugin contract):

ts
const plugin: IPlugin = {
  manifest,
  extensions: [
    {
      id: "blog-setup-dialog",
      target: "panel",
      point: "plugin-setup-dialogs",
      component: BlogSetupDialog,
      priority: 10,
      roles: ["admin"],
    },
  ],
  widgets: [
    {
      id: "blog-stats",
      name: "Blog Stats",
      area: "dashboard-stats",
      component: BlogStatsWidget,
      priority: 20,
      defaultColSpan: 2,
      defaultRowSpan: 1,
      roles: ["admin"],
    },
  ],
};
  • PluginExtension: id, target, point, component, optional priority, metadata, roles, permissions.
  • PluginWidget: id, name, area, component, optional priority, defaultColSpan, defaultRowSpan, roles, permissions.

Items without a priority sort as 100. Role/permission annotations are applied by the registry filter, but they are not a substitute for server-side authorization of the data or actions a component performs.

Extension points (server)#

PluginExtensionPoint (alias ExtensionPoint) is an async Server Component that resolves and renders the components registered for a target/point.

tsx
import { ExtensionPoint } from "@veap/plugins/server";

<ExtensionPoint target="app" point="navbar" mode="single" fallback={<DefaultNavbar />} />;
PropTypeNotes
targetstringRequired. The host surface, e.g. "app", "panel".
pointstringRequired. The named slot within the target.
mode"single" | "multiple"Default "multiple". single renders only the highest-priority component.
classNamestringApplied to the wrapper element.
propsanySpread onto each rendered component.
fallbackReactNodeRendered when no component matches.
childrenReactNodeAlternative fallback (takes precedence over fallback).
asElementTypeWrapper element; defaults to div when className is set.
includeDisabledbooleanDefault false; when true, disabled plugins are also scanned.

The server component waits for an in-flight bootstrap promise, reads the current user's roles/permissions, and returns the fallback when the plugins context is not bound yet. It renders extensions[0] in single mode.

Widget areas (server)#

PluginWidgetArea (alias WidgetArea) is an async Server Component that renders all widgets registered for an area, with the same user filtering.

tsx
import { WidgetArea } from "@veap/plugins/server";

<WidgetArea area="dashboard-stats" className="grid" fallback={<Empty />} />;

Props: area (required), className, props, fallback, as (default div).

Widget composer (server)#

WidgetComposer renders a user-arrangeable widget grid for a slot. It loads the default widgets for the slot, reads the user's saved layout (see the getUserWidgetsState action), and hands both to the client composer.

tsx
import { WidgetComposer } from "@veap/plugins/server";

<WidgetComposer slot="dashboard-main" columns={4} fallback={<Empty />} />;

Props: slot (required), columns (default 4), fallback.

The client counterpart is WidgetComposerClient; see Client hooks and components for the interactive drag/resize editor and how layouts are saved.

Resolution behavior#

Both PluginExtensionPoint and PluginWidgetArea resolve through PluginRegistry.getExtensions() / getWidgets(), which:

  • scan enabled plugins only (unless includeDisabled is set),
  • apply role/permission filtering against the current user,
  • sort by priority (default 100).

The area/slot data is loaded on every render; the widgets are not cached across requests by these components.

Client-side rendering#

If a slot must re-render when a plugin is toggled at runtime without a page reload, use the client variants (ExtensionPointClient, WidgetAreaClient, usePluginExtensions, usePluginWidgets) described in Client hooks and components. They fetch the same data through server actions and subscribe to onPluginsChanged.

veap
Preparing0%