Hi ABP Team,
We're reaching out regarding a customization scenario for our LeptonX-based application and would appreciate your guidance on the officially supported approach.
We're building a custom shell on Angular + ABP (LeptonX) and want to swap out specific LPX UI components for our own, while keeping their underlying ABP functionality (routing, auth, permissions, theme, language) intact. We need official guidance/APIs to do this safely, since the current public docs don't cover source-level replacement of these components.
What we want to do
- Replace
lpx-toolbar-containerwith our own component, while preserving its current functionality (user info, login/logout/profile actions, and the ability to add custom menu entries). - Extract the features from
lpx-settings(theme switching, language switching, and control over which setting groups appear) and move them under ourlpx-toolbar-containerreplacement, rather than replacinglpx-settingsas a separate component. - Replace
lpx-navbarwith our own component, while preserving its current functionality (active route detection, expand/collapse state, grouped/child menu items, permission-based visibility). - Replace the overall application layout (currently rendered via
abp-dynamic-layout) with our own layout component, while preserving ABP's route integration, permission filtering, and localization behavior.
What we're asking ABP for
- The officially supported way to replace each of these components (e.g., via
ReplaceableComponentsService/eThemeLeptonXComponents.*keys, or another mechanism). - The underlying service(s) that power
lpx-settings,lpx-toolbar-container, andlpx-navbar(theme/language state, setting groups, user info, menu/route data) so we can reuse that logic inside our own components. - The stable service contracts we should rely on (menu, user, route, permission services) so our replacements keep working across LeptonX updates.
- Source access or a working sample repo, if available, since current public APIs aren't enough for these replacements.
3 Answer(s)
-
0
- For Angular + LeptonX, the officially supported mechanism to swap LeptonX layout pieces (toolbar area, nav, breadcrumb, footer, etc.) is ABP Angular Component Replacement via
ReplaceableComponentsService+ LeptonX component keys (eThemeLeptonXComponents.*). You register your replacement at app startup, and LeptonX renders your component in that slot.
// e.g. app.component.ts (or an app initializer) import { ReplaceableComponentsService } from '@abp/ng.core'; import { eThemeLeptonXComponents } from '@volosoft/abp.ng.theme.lepton-x'; // LeptonX (commercial) import { MyToolbarContainerComponent } from './my-toolbar-container.component'; constructor(replaceable: ReplaceableComponentsService) { replaceable.add({ key: eThemeLeptonXComponents.Toolbar, // slot key component: MyToolbarContainerComponent, // your implementation }); }For replacing the overall application layout (what you described as “currently rendered via
abp-dynamic-layout”): ABP’s supported way is to replace the layout component (still using component replacement). Practically, your root template can stay as<abp-dynamic-layout />, while you replace the layout slot used by LeptonX to render the shell. (This keeps ABP’s route integration intact because routing is still driven by Angular router + ABP packages; the layout is just a host.)For “extract features from
lpx-settings(theme + language)” and move them into your toolbar replacement:- Language: ABP Angular’s stable state/services are in
@abp/ng.core(commonlySessionStateServicefor language selection +ConfigStateServicefor reading available languages). This is the same pattern used in ABP Angular UI custom header examples (language list comes from config state, selected culture from session state). - Theme switching: LeptonX theme selection is handled by the LeptonX theme packages. The stable/official guidance is: if you need behavior beyond the exposed UI, you either (a) replace the LeptonX UI piece via component replacement and reuse the public services exposed by the package, or (b) pull theme source code and build against it (see “Source access” below). Public documentation does not fully enumerate internal theme-service contracts for
lpx-settings(so relying on undocumented internals is not upgrade-safe).
- Language: ABP Angular’s stable state/services are in
For replacing
lpx-navbarwhile keeping “active route detection, collapse state, grouped/child menu items, permission-based visibility”:- The upgrade-safe way is still: replace the navbar slot and build your navbar using ABP Angular’s menu/permission infrastructure (rather than copying LeptonX internal navbar logic).
- ABP Angular provides permission-aware rendering helpers (e.g.,
*abpPermission="...") and menu data is provided by ABP’s Angular packages; LeptonX’s navbar is ultimately a renderer over that data. (Public docs do not provide a “supported internal navbar service contract” for LeptonX itself; so the supported approach is replace the UI and consume ABP Angular’s menu/permission APIs rather than LeptonX internals.)
For toolbar user info + login/logout/profile actions + custom entries:
- If you only need to customize the user dropdown items, ABP Angular provides
UserMenuServiceto remove/patch/add items (this is used in official support guidance; the key point is to apply changes aftercurrentUserbecomes authenticated, because the menu can be (re)built around login/tenant changes). - If you need to replace the entire toolbar container, do it with component replacement and implement user actions in your component (calling ABP
AuthService/ navigation helpers as needed).
- If you only need to customize the user dropdown items, ABP Angular provides
Source access / sample:
- Official support guidance is: you can download LeptonX source code via ABP Suite (commercial customers). This is the intended way if you need to clone LeptonX components and customize deeply, beyond what public APIs expose.
- There is also a documented CLI approach in community material (
abp get-source Volo.Abp.LeptonXTheme), but the verified/official support answer we can cite is: ABP Suite download source code.
Practical mapping to your 4 goals (what’s officially safe)
- Replace
lpx-toolbar-container
- Supported:
ReplaceableComponentsService+ the relevanteThemeLeptonXComponents.*key (toolbar slot). - Then implement your own toolbar and use ABP Angular services (auth/current user/menu/user-menu) inside.
- Move theme/language out of
lpx-settingsand into your toolbar
- Supported: Replace toolbar UI and implement language switching via ABP Angular session/config services.
- Theme switching: either use the public LeptonX theming APIs if they are exposed by your LeptonX version, or pull LeptonX source code and reuse its implementation. Public docs do not fully describe “setting groups” APIs for
lpx-settingsas stable contracts.
- Replace
lpx-navbar
- Supported: Replace the navbar slot via
ReplaceableComponentsService, and render menu items using ABP Angular menu + permission primitives (rather than depending on LeptonX internal navbar services).
- Replace the overall layout (
abp-dynamic-layouthost)
- Supported: ABP Angular “replace a layout” mechanism (component replacement) while keeping
<abp-dynamic-layout />at the root so ABP keeps routing/layout resolution.
Where to get the component code to clone
- Officially supported: Download LeptonX source code via ABP Suite, then copy/modify the components you want and register them via component replacement.
Sources
- https://docs.abp.io/en/abp/latest/UI/Angular/Component-Replacement
- https://docs.abp.io/en/commercial/latest/themes/lepton-x/angular#customization
- https://abp.io/support/questions/7143
- https://abp.io/support/questions/4358
- https://abp.io/support/questions/10591
- https://abp.io/docs/10.4/ui-themes/lepton-x-lite/angular
This response was generated by AI. If it didn’t solve your problem, feel free to post a new answer — our support team will jump in to help you soon.
Markdown supported.Copy, paste, or drag & drop images and files (max 100 MB per file, 100 MB total per post) - For Angular + LeptonX, the officially supported mechanism to swap LeptonX layout pieces (toolbar area, nav, breadcrumb, footer, etc.) is ABP Angular Component Replacement via
-
0
Hi,
All four are supported through
ReplaceableComponentsServicewith the LeptonX component keys, and the data behind those components comes from services you can inject directly.Keys
| What you replace |
eThemeLeptonXComponentskey | |---|---| |lpx-toolbar-container|Theme.ToolbarComponent| |lpx-navbar(desktop sidebar) |Theme.NavbarComponent| |lpx-settings|Theme.SettingsComponent| | whole application layout |Theme.ApplicationLayoutComponent|Two things about the toolbar slot: the user avatar and the profile dropdown live inside
lpx-toolbar-container, so replacing it means you rebuild those controls yourself. The settings panel is a sibling slot and keeps rendering unless you also replaceTheme.SettingsComponent. The mobile sidebar isTheme.MobileNavbarComponent, separate fromTheme.NavbarComponent.Where to register
Register the replacements in
AppComponent's constructor. That runs after all app initializers and beforeabp-dynamic-layoutcreates the layout, so you don't depend on provider ordering:@Component({ selector: 'app-root', template: ` <abp-loader-bar /> <abp-dynamic-layout /> `, imports: [LoaderBarComponent, DynamicLayoutComponent], }) export class AppComponent { private readonly replaceable = inject(ReplaceableComponentsService); constructor() { // keep the stock shell, swap individual slots this.replaceable.add({ key: eThemeLeptonXComponents.Toolbar, component: MyToolbarComponent }); this.replaceable.add({ key: eThemeLeptonXComponents.Navbar, component: MyNavbarComponent }); // or take over the whole shell instead — see the last section // this.replaceable.add({ key: eThemeLeptonXComponents.ApplicationLayout, component: MyLayoutComponent }); } }Pick one of the two: once
Theme.ApplicationLayoutComponentis replaced, the stock layout is gone and nothing consumes the slot keys anymore.Keep
<abp-dynamic-layout />in the template — it picks the layout from route metadata through the same store. Register before the first layout resolution:DynamicLayoutComponentkeeps the resolved layout while the layout type stays the same, so a replacement added later won't be picked up.Services to inject
| Data | Service | Package | |---|---|---| | Menu items, groups |
NavbarService(navbarItems$,groupedNavbarItems$) |@volo/ngx-lepton-x.core| | Toolbar entries |ToolbarService.items$, fed from ABPNavItemsService.addItems()|@volo/ngx-lepton-x.core,@abp/ng.theme.shared| | User info + profile actions |UserProfileService.user$(user.userActionGroups) |@volo/ngx-lepton-x.core| | User menu entries |UserMenuService(addItems/patchItem/removeItem) |@abp/ng.theme.shared| | Authentication, login/logout |AuthService(isAuthenticated,navigateToLogin(),logout()) |@abp/ng.core| | Theme |ThemeService(styles$,selectedStyle$,setTheme()) |@volosoft/ngx-lepton-x| | Language |LanguageService(languages$,selectedLanguage$,setSelectedLanguage()) |@volo/ngx-lepton-x.core|These are all public exports of the package entry points in your version. Four details that bite once you render the data yourself:
Permission filtering is applied upstream: ABP's
RoutesService.visible$filters byrequiredPolicyand feedsNavbarService, so a route the user can't see never reaches your component. It re-runs when the application configuration refreshes (login, tenant switch, page reload) — a permission changed on the server in another session won't push into the menu until then.Active route and expand/collapse state: on your version this is computed by
lpx-navbar-routes(NavbarRoutesComponent), not byNavbarService—NavbarServiceapplies it once, on the first navigation. It moved intoNavbarServicein 5.6.0. Rendering the items through that component inside your navbar works on both:<lpx-navbar-routes [navbarItems]="navbarService.navbarItems$ | async" [groupedItems]="navbarService.groupedNavbarItems$ | async" />It renders the menu list only — the logo, collapse behaviour and menu filter of
lpx-navbarare not part of it. Two cases where you need to track the route yourself: your own markup instead of this component, and grouped menus, because the grouped branch rendersgroupedItemsas given.visiblepredicates: toolbar items and user actions can carry avisiblepredicate returning a boolean, a Promise or an Observable, andAbpToolbarServiceonly appliesrequiredPolicy. Evaluate it withLpxVisibleDirective(@volo/ngx-lepton-x.core) the way the stock components do — injectInjectorand use*lpxVisible="!item.visible || item.visible(item, injector)".Localization: entries coming from
UserMenuServicecarry raw keys (AbpAccount::MyAccount), so pipe them through| abpLocalization. Navbar item texts are already localized, but thegroupvalue on a navbar item is still a raw key, so localize it if you render grouped menus yourself.
AuthService.logout()returns an Observable, so subscribe to it — otherwise nothing runs.Which setting groups show up
That's a DI token rather than a component.
lpx-settingsrenders whateverLPX_SETTINGS_SERVICEprovides, so wrap the default service and filter its two streams (LPX_SETTINGS_SERVICE,ISettingsServiceandSettingsServiceall come from@volosoft/ngx-lepton-x/layouts):@Injectable() export class CustomSettingsService implements ISettingsService { private defaults = inject(SettingsService); private themeService = inject(ThemeService); settings$ = this.defaults.settings$.pipe( map(groups => groups.filter(group => group.id === this.themeService.id)), ); selectedSettings$ = this.defaults.selectedSettings$.pipe( map(groups => groups.filter(group => group.id === this.themeService.id)), ); }Register it after
provideSideMenuLayout()so it wins over the default:provideThemeLeptonX(), provideSideMenuLayout(), { provide: LPX_SETTINGS_SERVICE, useClass: CustomSettingsService },For your second goal you don't need
lpx-settingsat all — injectThemeServiceandLanguageServiceinto your toolbar replacement and build the controls there. If you keep the stock layout, replaceTheme.SettingsComponentwith an empty component so the old panel stops rendering next to your toolbar.Replacing the whole layout
Once you take over
Theme.ApplicationLayoutComponent, the stock layout is gone and nothing consumes the slot keys anymore — your components go into your own template. There are two ways to write that template.Keep
<lpx-layout>(SideMenuLayoutComponentfrom@volosoft/ngx-lepton-x/layouts) and fill only the panels you want to change. This is what the stock ABP layout does, and it keeps the responsive breakpoints, the wrapper/container classes, the layout setting group and<lpx-topbar-content>working, with defaults for everything you don't fill. The panel directives come from@volo/ngx-lepton-x.core:lpx-navbar-panel,lpx-content,lpx-breadcrumb-panel,lpx-toolbar-panel,lpx-settings-panel,lpx-footer-panel,lpx-logo-panel,lpx-current-user-image-panel,lpx-mobile-navbar-panel,lpx-mobile-navbar-settings-panel,lpx-mobile-navbar-profile-panel.Or write the shell from scratch, in which case everything you want is yours to place:
<router-outlet>,abp-page-alert-container, the desktop navbar, breadcrumb, toolbar, settings,<lpx-topbar-content>(otherwise entries added throughTopbarContentServicedisappear), footer, logo, the mobile navbar with its settings and profile panels, and the responsive/container classes. Routing, permissions, theme and localization keep working either way, but as services you consume, not as behaviour the layout gives you for free.Source code
abp get-source Volo.Abp.LeptonXTheme -v <your LeptonX version>gives you anangular/projects/volo-lepton-xfolder with the full TS/HTML of@volosoft/abp.ng.theme.lepton-x— the side-menu and top-menu layout components and their providers, which is the best reference for how the slots are wired. Pass the version explicitly, otherwise you get the latest one and it won't match what you run. The lower-level UI kit (lpx-navbar,lpx-toolbar-container,lpx-settings) isn't in that package, but the published packages ship their original sources inside the sourcemaps undernode_modulesif you want to read those implementations — as a reference only, the supported contract is what the package entry points export.Thanks
Markdown supported.Copy, paste, or drag & drop images and files (max 100 MB per file, 100 MB total per post) -
0
This should be enough, thanks
Markdown supported.Copy, paste, or drag & drop images and files (max 100 MB per file, 100 MB total per post)