Framework-agnostic widget protocol for embedding micro-frontends via iframes.
Part of the Urule ecosystem — the open-source coordination layer for AI agents.
- Host and Client bridges for bidirectional postMessage communication between the shell and iframe widgets
- Widget manifest schema with validation for mount points, permissions, semver, and entry types (
nativeorexternal) - Widget registry for registering manifests, creating instances, and querying by workspace, mount point, category, or entry type
- Built-in theme with dark mode defaults (Urule brand colors)
- Mount points:
sidebar,main-panel,modal,status-bar,drawer - Permission model using
scope:actionstrings with validation - Zero framework dependencies -- works with React, Vue, Svelte, vanilla JS, or any frontend
npm install @urule/widget-sdkimport { WidgetHostBridge } from '@urule/widget-sdk';
const iframe = document.getElementById('my-widget') as HTMLIFrameElement;
const bridge = new WidgetHostBridge(iframe, 'widget-001');
bridge.onReady(() => console.log('Widget is ready'));
bridge.onAction((action) => console.log('Widget action:', action));
bridge.onDataRequest((req) => {
// Respond with data
bridge.sendData({ resource: req.resource, data: { /* ... */ } });
});
bridge.init({
widgetId: 'widget-001',
manifestId: 'my-manifest',
workspaceId: 'ws-1',
config: {},
theme: DEFAULT_URULE_THEME,
permissions: ['data:read'],
});import { WidgetClientBridge } from '@urule/widget-sdk';
const bridge = new WidgetClientBridge('widget-001');
bridge.onInit((payload) => {
console.log('Initialized with config:', payload.config);
bridge.ready();
});
bridge.onTheme((theme) => {
document.body.style.background = theme.colors.background;
});
bridge.onDestroy(() => bridge.destroy());import { validateManifest } from '@urule/widget-sdk';
const result = validateManifest({
id: 'my-widget',
name: 'My Widget',
version: '1.0.0',
description: 'A sample widget',
author: 'Urule',
mountPoints: ['sidebar'],
entryType: 'external',
entryUrl: 'https://example.com/widget',
permissions: ['data:read'],
defaultConfig: {},
});
if (!result.valid) {
console.error(result.errors);
}| Export | Description |
|---|---|
WidgetHostBridge |
Host-side bridge -- sends init, config, theme, data, navigate, and destroy messages to an iframe |
WidgetClientBridge |
Widget-side bridge -- signals readiness, resize, actions, and data requests to the host |
WidgetRegistry |
In-memory registry for manifests and widget instances with filtering and CRUD |
validateManifest(manifest) |
Validates a widget manifest object, returns { valid, errors } |
validatePermissions(perms) |
Validates an array of permission strings (scope:action format) |
DEFAULT_URULE_THEME |
Default dark theme object |
| Type | Description |
|---|---|
WidgetManifest |
Full widget manifest with id, name, version, mountPoints, entryType, permissions, etc. |
WidgetInstance |
A placed widget in a workspace with config, position, and mount point |
WidgetRenderContext |
Context passed to a rendered widget (theme, config, permissions) |
BridgeMessage<T> |
Envelope for all postMessage communication |
WidgetTheme |
Theme definition with colors, fonts, and light/dark mode |
- Create a manifest JSON describing your widget (id, name, mount points, permissions)
- Validate the manifest with
validateManifest() - In your widget's entry point, create a
WidgetClientBridgeand callbridge.ready()after initialization - Listen for
onInit,onConfig,onTheme, andonDestroyevents - Use
bridge.sendAction()to communicate user actions back to the host - Use
bridge.requestData()to fetch data from the host application
Widgets can be built with any framework. The only requirement is postMessage compatibility.
See CONTRIBUTING.md for development setup and guidelines.
Apache-2.0