App Studio
Widget Registry
The Widget Registry is the global catalog of all Widget Definitions available in App Studio — system widgets and custom widgets. It drives the widget palette and provides config schemas to the Properties Editor.
Registry Responsibilities
- Discovery — maintains the list of all available widget types shown in the palette
- Schema serving — provides the
configSchemafor each widget type so the Properties Editor can auto-generate config forms - Federation loading — loads custom widget React components via Module Federation at runtime
- Tenant overrides — stores and applies Level 2 (tenant-level) config overrides per widget type
- Compatibility checking — during app import, validates that all widget types referenced in the bundle are available in the target tenant
Registry API
| Endpoint | Description |
|---|---|
GET /api/widget-registry | List all registered widget definitions (used by the builder palette) |
GET /api/widget-registry/{typeId} | Get a specific widget definition including full config schema |
POST /api/widget-registry/register | Register a new custom widget (requires platform admin role) |
PUT /api/widget-registry/{typeId}/tenant-override | Set tenant-level config override for a widget type |
DELETE /api/widget-registry/{typeId} | Unregister a custom widget (system widgets cannot be deleted) |
Registry in the Frontend
The TypeScript WidgetRegistry class manages the client-side catalog:
// WidgetRegistry.ts (simplified)
class WidgetRegistry {
private definitions = new Map<string, WidgetDefinition>();
register(def: WidgetDefinition): void {
this.definitions.set(def.widgetTypeId, def);
}
get(typeId: string): WidgetDefinition | undefined {
return this.definitions.get(typeId);
}
getAll(): WidgetDefinition[] {
return Array.from(this.definitions.values());
}
async loadFederatedWidget(typeId: string): Promise<React.ComponentType> {
const def = this.get(typeId);
if (!def?.federationUrl) throw new Error(`No federation for ${typeId}`);
// Dynamically load the remote module via Webpack Module Federation
const container = await loadRemote(def.federationUrl);
return container.get(def.federationModule);
}
}
export const widgetRegistry = new WidgetRegistry();
System Widget Registration
System widgets are registered at application startup in the App Studio bootstrap:
// App Studio bootstrap
import { widgetRegistry } from './WidgetRegistry';
import { DataGridDefinition } from './widgets/DataGrid';
import { ChartDefinition } from './widgets/Chart';
import { MetricDefinition } from './widgets/Metric';
// ... all system widgets
export function bootstrapWidgets(): void {
widgetRegistry.register(DataGridDefinition);
widgetRegistry.register(ChartDefinition);
widgetRegistry.register(MetricDefinition);
widgetRegistry.register(FormDefinition);
widgetRegistry.register(ButtonDefinition);
widgetRegistry.register(TextDefinition);
// ...
}
Registry Caching
Widget definitions are cached in Redis on the backend (WidgetService.cs) with a long TTL (1 hour). The frontend fetches the list on builder startup and caches it in memory for the session. Changes to custom widget registrations take effect after the next builder session or cache invalidation.