Skip to main content

Plugin UI System Overview

The Plugin UI System (@gauzy/plugin-ui) is a frontend plugin architecture that lets you extend the Ever Gauzy UI with new pages, navigation items, dashboard widgets, settings tabs, and more β€” without modifying the core application.

note

This is the compile-time UI plugin system for built-in frontend extensions. For the runtime Marketplace Plugin System (npm/CDN installation, lifecycle management), see the Marketplace Plugin System documentation.

Architecture​

Package Overview​

The plugin UI system is split across four packages with clear responsibilities:

PackagePurposeDependencies
@gauzy/plugin-uiCore plugin infrastructure β€” registry, lifecycle, extensions, events, i18nAngular only
@gauzy/ui-reactReact-to-Angular bridge β€” directives, hooks, error boundaryAngular + React 19
@gauzy/ui-react-componentsReusable React UI primitives (Card, Progress, etc.)React only
@gauzy/plugin-*-react-uiIndividual React plugins (e.g., dashboard time tracking)ui-react + ui-react-components

Dependency Graph​

Key rule: @gauzy/plugin-ui has no React dependency. React lives only in @gauzy/ui-react and downstream.

Core Concepts​

Plugin Definition​

Every UI plugin is a PluginUiDefinition β€” a configuration object that declares what the plugin contributes:

import { defineDeclarativePlugin } from '@gauzy/plugin-ui';

export const MyPlugin = defineDeclarativePlugin('my-plugin', {
routes: [{ path: 'my-page', component: MyPageComponent }],
navMenu: [{ title: 'My Page', icon: 'star-outline', link: '/pages/my-page' }],
extensions: [{ id: 'my-widget', slotId: 'dashboard-widgets', component: MyWidgetComponent }]
});

Plugin Types​

TypeUse CaseFunction
DeclarativeSimple plugins with routes/nav/tabs onlydefineDeclarativePlugin()
Module-basedPlugins needing Angular DI, services, providersdefinePlugin()
Lazy moduleLarge plugins loaded on demanddefinePlugin() with loadModule
Plugin groupParent plugin with child pluginsdefinePluginGroup()

Extension Slots​

Extension slots are named insertion points in the UI where plugins can contribute components:

// Plugin registers a widget into the dashboard
registry.register({
id: 'time-tracking-widget',
slotId: PAGE_EXTENSION_SLOTS.DASHBOARD_WIDGETS,
component: TimeTrackingWidgetComponent,
order: 10
});

Built-in slots include DASHBOARD_WIDGETS, DASHBOARD_WINDOWS, SETTINGS_TABS, HEADER_TOOLBAR, SIDEBAR_FOOTER, and more.

Lifecycle Hooks​

Plugins can implement lifecycle interfaces for initialization and cleanup:

export class MyPluginModule implements IOnPluginUiBootstrap, IOnPluginUiDestroy {
ngOnPluginBootstrap() {
// Called after plugin instantiation
}

ngOnPluginDestroy() {
// Called before plugin teardown
}
}

Cross-Plugin Communication​

Plugins communicate via a type-safe event bus:

// Define an event contract
export const DataRefreshed = definePluginEvent<{ timestamp: number }>('my-plugin', 'my-plugin:data-refreshed');

// Emit
const handle = bindEventToBus(DataRefreshed, eventBus);
handle.emit({ timestamp: Date.now() });

// Subscribe
handle.on().subscribe(event => console.log('Refreshed at', event.payload.timestamp));

Configuration​

Plugins are registered in the application's plugin-ui.config.ts:

import { PluginUiConfig } from '@gauzy/plugin-ui';

export const uiPluginConfig: PluginUiConfig = {
defaultLanguage: 'en',
defaultLocale: 'en-US',
availableLanguages: ['en', 'bg', 'he', 'ru'],
availableLocales: ['en-US', 'bg-BG', 'he-IL', 'ru-RU'],
startWeekOn: 1, // Monday
plugins: [
MyPlugin,
AnotherPlugin.init({ plugins: [ChildPluginA, ChildPluginB] })
]
};

The host application initializes the system in its bootstrap module by passing service types (not instances) that bridge the plugin system to the host:

@NgModule({
imports: [
PluginUiModule.init({
navBuilder: NavMenuBuilderService, // Implements IDeclarativeNavBuilder
routeRegistry: PageRouteRegistryService, // Implements IDeclarativePageRouteRegistry
tabRegistry: PageTabRegistryService, // Implements IDeclarativePageTabRegistry
translateService: TranslateAdapterService,// Implements IPluginTranslateService
permissionChecker: PermissionAdapterService, // Implements IPluginPermissionChecker
featureChecker: FeatureAdapterService // Implements IPluginFeatureChecker
})
]
})
export class BootstrapModule {}

All six services are optional β€” the system works without them but specific features (navigation, routes, tabs, translations, permission gating, feature flags) require the corresponding service.

The PluginUiServices interface:

interface PluginUiServices {
navBuilder?: Type<IDeclarativeNavBuilder>;
routeRegistry?: Type<IDeclarativePageRouteRegistry>;
tabRegistry?: Type<IDeclarativePageTabRegistry>;
translateService?: Type<IPluginTranslateService>;
permissionChecker?: Type<IPluginPermissionChecker>;
featureChecker?: Type<IPluginFeatureChecker>;
}

Bootstrap Flow (Detailed)​

When PluginUiModule.bootstrapPlugins() runs, it follows this exact sequence:

  1. Validate β€” validatePluginDependencies() checks for missing deps and circular references
  2. Version check β€” checkVersionCompatibility() verifies peerPlugins version ranges
  3. Order β€” Topological sort by dependency graph
  4. Instantiate β€” Create module instances (module-based) or call bootstrap() (declarative)
  5. Register β€” Store instances in PluginUiRegistryService
  6. Record health β€” PluginHealthService tracks boot time per plugin
  7. Bootstrap hooks β€” Call ngOnPluginBootstrap() on all plugins in order
  8. After-bootstrap hooks β€” Call ngOnPluginAfterBootstrap() on all plugins
  9. Preload β€” Plugins with loadStrategy: 'preload' load in the background

If any plugin fails, the error is recorded in PluginHealthService and the remaining plugins continue to bootstrap.