Open Closed

LeptonX Custom Layout and Replaceable Sources #10845


User avatar
0
MarkHulevskyi created

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

  1. Replace lpx-toolbar-container with our own component, while preserving its current functionality (user info, login/logout/profile actions, and the ability to add custom menu entries).
  2. Extract the features from lpx-settings (theme switching, language switching, and control over which setting groups appear) and move them under our lpx-toolbar-container replacement, rather than replacing lpx-settings as a separate component.
  3. Replace lpx-navbar with our own component, while preserving its current functionality (active route detection, expand/collapse state, grouped/child menu items, permission-based visibility).
  4. 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, and lpx-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.
Markdown supported.
Copy, paste, or drag & drop images and files (max 100 MB per file, 100 MB total per post)

3 Answer(s)
  • User Avatar
    0
    AI-Bot created
    Support Team ABP Agent
    • 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 (commonly SessionStateService for language selection + ConfigStateService for 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).
    • For replacing lpx-navbar while 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 UserMenuService to remove/patch/add items (this is used in official support guidance; the key point is to apply changes after currentUser becomes 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).
    • 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)

    1. Replace lpx-toolbar-container
    • Supported: ReplaceableComponentsService + the relevant eThemeLeptonXComponents.* key (toolbar slot).
    • Then implement your own toolbar and use ABP Angular services (auth/current user/menu/user-menu) inside.
    1. Move theme/language out of lpx-settings and 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-settings as stable contracts.
    1. 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).
    1. Replace the overall layout (abp-dynamic-layout host)
    • 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)
  • User Avatar
    0
    maliming created
    Support Team Fullstack Developer

    Hi,

    All four are supported through ReplaceableComponentsService with the LeptonX component keys, and the data behind those components comes from services you can inject directly.

    Keys

    | What you replace | eThemeLeptonXComponents key | |---|---| | 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 replace Theme.SettingsComponent. The mobile sidebar is Theme.MobileNavbarComponent, separate from Theme.NavbarComponent.

    Where to register

    Register the replacements in AppComponent's constructor. That runs after all app initializers and before abp-dynamic-layout creates the layout, so you don't depend on provider ordering:

    @Component({
      selector: 'app-root',
      template: `
        &lt;abp-loader-bar /&gt;
        &lt;abp-dynamic-layout /&gt;
      `,
      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.ApplicationLayoutComponent is 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: DynamicLayoutComponent keeps 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 ABP NavItemsService.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 by requiredPolicy and feeds NavbarService, 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 by NavbarServiceNavbarService applies it once, on the first navigation. It moved into NavbarService in 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-navbar are 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 renders groupedItems as given.

    • visible predicates: toolbar items and user actions can carry a visible predicate returning a boolean, a Promise or an Observable, and AbpToolbarService only applies requiredPolicy. Evaluate it with LpxVisibleDirective (@volo/ngx-lepton-x.core) the way the stock components do — inject Injector and use *lpxVisible="!item.visible || item.visible(item, injector)".

    • Localization: entries coming from UserMenuService carry raw keys (AbpAccount::MyAccount), so pipe them through | abpLocalization. Navbar item texts are already localized, but the group value 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-settings renders whatever LPX_SETTINGS_SERVICE provides, so wrap the default service and filter its two streams (LPX_SETTINGS_SERVICE, ISettingsService and SettingsService all 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-settings at all — inject ThemeService and LanguageService into your toolbar replacement and build the controls there. If you keep the stock layout, replace Theme.SettingsComponent with 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> (SideMenuLayoutComponent from @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 through TopbarContentService disappear), 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 an angular/projects/volo-lepton-x folder 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 under node_modules if 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)
  • User Avatar
    0
    MarkHulevskyi created

    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)
Boost Your Development
ABP Live Training
Packages
See Trainings
Mastering ABP Framework Book
The Official Guide
Mastering
ABP Framework
Learn More
Mastering ABP Framework Book
Made with ❤️ on ABP v10.8.0-preview. Updated on September 16, 2026, 14:50
1
ABP Assistant
🔐 You need to be logged in to use the chatbot. Please log in first.