Hello!
I am implementing tenant-specific branding in an ABP application with LeptonX Theme. The settings are saving correctly per tenant, but I cannot get the styles to apply using Layout Hooks.
Environment:
- ABP Version: 9.2
- UI Framework: Blazor WebApp (also tried with Auth Server MVC)
- Theme: LeptonX Theme
- Project Type: Microservice solution
What I'm trying to achieve: Apply custom colors per tenant using CSS variables injected into the page head.
What I've tried: Following these documentation links:
- https://abp.io/community/articles/white-labeling-in-abp-framework-5trwmrfm#gsc.tab=0
- https://abp.io/docs/latest/framework/ui/blazor/layout-hooks
The Problem:
Configure<AbpLayoutHookOptions>(options =>
{
    options.Add(
        LayoutHooks.Head.Last,  // This does NOT work - component never renders in my <Head> html
        typeof(AppearanceStylesComponent)
    );
    
    options.Add(
        LayoutHooks.Body.First,  // This WORKS - component renders correctly
        typeof(TestStyleComponent)
    );
});
16 Answer(s)
- 
    0To inject tenant-specific styles into the <head> using Layout Hooks in ABP with the LeptonX Theme, you must ensure that: - The layout you are using actually implements the LayoutHooks.Head.Last hook.
- Your component is registered and available in the correct assembly.
- The theme's layout is not overriding or missing the hook point.
 Steps to Diagnose and Fix1. Verify the Layout Implements the HookCheck the LeptonX layout file (e.g., /Themes/LeptonX/Layouts/Application/Default.cshtmlfor MVC or the corresponding Blazor layout) and ensure it contains:@await Component.InvokeLayoutHookAsync(LayoutHooks.Head.Last, StandardLayouts.Application)If this line is missing, the hook will not be triggered. You may need to override the layout in your project and add this line. 2. Register the Component CorrectlyYour configuration is correct: Configure<AbpLayoutHookOptions>(options => { options.Add(LayoutHooks.Head.Last, typeof(AppearanceStylesComponent)); });Ensure AppearanceStylesComponentis a valid view/component and is discoverable by the DI system.3. For Blazor: Head Tag LimitationsBlazor Server and WebAssembly have limitations with dynamic head content. The <head>section is not always re-rendered as expected. For Blazor, consider using the built-inHeadOutletor inject styles via JavaScript interop if the hook does not work.4. For MVC: Overriding the LayoutIf you are using MVC for the Auth Server, you may need to override the default layout to ensure the hook is present. 5. Theme NuGet Package UpdatesIf you are using the LeptonX NuGet package, updates may overwrite your changes. Always check the layout after updates. Example: Adding the Hook to the LayoutOverride the layout file in your project (if not already present): @await Component.InvokeLayoutHookAsync(LayoutHooks.Head.Last, StandardLayouts.Application)References
 If the hook is present in the layout and the component is registered, the style injection should work. If not, override the layout and add the hook explicitly. For Blazor, consider the limitations of head manipulation and use alternatives if necessary. 
 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. 
- 
    0hi What is the UI type of AppearanceStylesComponentandTestStyleComponentOn which page does the AppearanceStylesComponentnot work, and does theTestStyleComponentwork?The page is MVC or Blazor? Thanks. 
- 
    0Both are Blazor ComponentBase components in a Blazor WebApp project and I showed them just to show they work on Body but not on head. The issue is injection of these components through layout hooks Head.Last They were working with LayoutHooks.Body.First We are trying to apply white label colors per tenant. So it looks like this using MyProject.AdministrationService.Constants; using MyProject.AdministrationService.Features; using Microsoft.AspNetCore.Components; using Volo.Abp.Features; using Volo.Abp.Settings; namespace MyProject.Blazor.Shared.Components.Layout; public partial class AppearanceStyles : ComponentBase { [Inject] private ISettingProvider SettingProvider { get; set; } = null!; public string? PrimaryColor { get; private set; } public string? SecondaryColor { get; private set; } protected override async Task OnInitializedAsync() { try { var primaryColor = await SettingProvider.GetOrNullAsync(SettingDefinitionConstants.BrandingPrimaryColor); var secondaryColor = await SettingProvider.GetOrNullAsync(SettingDefinitionConstants.BrandingSecondaryColor); PrimaryColor = primaryColor ?? SettingDefinitionConstants.BrandingPrimaryColorDefaultValue; SecondaryColor = secondaryColor ?? SettingDefinitionConstants.BrandingSecondaryColorDefaultValue; } catch { PrimaryColor = null; SecondaryColor = null; } await base.OnInitializedAsync(); } } @using MyProject.AdministrationService.Constants @using MyProject.AdministrationService.Features @using Volo.Abp.Features @using Volo.Abp.Settings @if (!string.IsNullOrEmpty(PrimaryColor) || !string.IsNullOrEmpty(SecondaryColor)) { <style> :root { --myproject-primary-color: @PrimaryColor; --myproject-secondary-color: @SecondaryColor; } .btn-primary { background-color: var(--myproject-primary-color); border-color: var(--myproject-primary-color); } .btn-primary:hover { background-color: var(--myproject-primary-color); border-color: var(--myproject-primary-color); opacity: 0.8; } .btn-primary:focus { background-color: var(--myproject-primary-color); border-color: var(--myproject-primary-color); box-shadow: 0 0 0 0.25rem rgba(from var(--myproject-primary-color) r g b / 0.25); } .bg-primary { background-color: var(--myproject-primary-color) !important; } .text-primary { color: var(--myproject-primary-color) !important; } .border-primary { border-color: var(--myproject-primary-color) !important; } .btn-outline-primary { border-color: var(--myproject-primary-color); color: var(--myproject-primary-color); } .btn-outline-primary:hover { background-color: var(--myproject-primary-color); border-color: var(--myproject-primary-color); color: #fff; } .btn-outline-primary:focus { background-color: var(--myproject-primary-color); border-color: var(--myproject-primary-color); color: #fff; } .nav-pills .nav-link.active, .nav-pills .show > .nav-link { background-color: var(--myproject-primary-color); } .nav-link.active:hover { background-color: var(--myproject-primary-color); opacity: 0.8; } /* Secondary Color Styles */ .btn-secondary { background-color: var(--myproject-secondary-color); border-color: var(--myproject-secondary-color); } .btn-secondary:hover { background-color: var(--myproject-secondary-color); border-color: var(--myproject-secondary-color); opacity: 0.8; } .btn-secondary:focus { background-color: var(--myproject-secondary-color); border-color: var(--myproject-secondary-color); box-shadow: 0 0 0 0.25rem rgba(from var(--myproject-secondary-color) r g b / 0.25); } .bg-secondary { background-color: var(--myproject-secondary-color) !important; } .text-secondary { color: var(--myproject-secondary-color) !important; } .border-secondary { border-color: var(--myproject-secondary-color) !important; } .btn-outline-secondary { border-color: var(--myproject-secondary-color); color: var(--myproject-secondary-color); } .btn-outline-secondary:hover { background-color: var(--myproject-secondary-color); border-color: var(--myproject-secondary-color); color: #fff; } .btn-outline-secondary:focus { background-color: var(--myproject-secondary-color); border-color: var(--myproject-secondary-color); color: #fff; } </style> } Configure<AbpLayoutHookOptions>(options => { options.Add( LayoutHooks.Head.Last, typeof(AppearanceStyles) ); });Also had the same issue in MVC by using public class BootstrapStyleViewComponent(ISettingProvider settingProvider) : AbpViewComponent { public async Task<IViewComponentResult> InvokeAsync() { var primaryColor = await settingProvider.GetOrNullAsync(WhileLabelAppSettings.PrimaryColor); return View("~/Components/BootstrapStyle/Default.cshtml", primaryColor); } }And also Configure<AbpLayoutHookOptions>(options => { options.Add( LayoutHooks.Head.Last, typeof(BootstrapStyleViewComponent)); });
- 
    0hi Thanks. Can you share a template project that can reproduce your hook problem? liming.ma@volosoft.com I will check the source code and provide a solution. 
- 
    0Hello, sent thank you. 
- 
    0hi Can you share whole template project source code? Thanks. 
- 
    0I emailed you the whole project source code at liming.ma@volosoft.com 3 days ago it's also very easy to reproduce.. 2 files to change 
- 
    0hi I see, I will try to reproduce it. Thanks. 
- 
    0
- 
    0
- 
    0Thanks it works! we thought it would work out of the box with leptonx theme side menu 
- 
    0Great 👍 
- 
    0Hello! sorry I'm reopening this only because I have the exact same issue with the mobile app Maui Blazor hybrid. We use Leptonx theme side menu using Volo.Abp.AspNetCore.Components.MauiBlazor.LeptonXTheme; and exact same Configure<AbpLayoutHookOptions>(options => { options.Add( LayoutHooks.Head.Last, typeof(StyleHook) ); });I tried adding this in App.razor <LayoutHook Name="@LayoutHooks.Head.Last" Layout="@StandardLayouts.Application" />Thank you! 
- 
    0hi The Maui Blazor hybrid has an Index.htmlin thewwwrootfolder.It's static HTML page. You can add HTML/JS/CSS to it. Thanks. 
- 
    0Thank you for the quick answer, very appreciated. so it could still be possible to inject tenant style through other things than layout hooks. Like javascript injection. Have a good day 
- 
    0Have a good day too. : ) 



 
                                