Search code examples
c#authenticationblazor.net-6.0blazor-server-side

Styling Identity Scaffolded Razor Pages to use default Blazor theme


I just added Identity to my existing Blazor Server project using Identity Scaffolding. I followed this Microsoft learn guide here.

However, since Identity uses Razor pages instead of Razor components, the styling of the UI changes when a visitor navigates between Identity pages and components.

For eg: The page initially loads like this:

And when the user hits the Login link at the top right corner, the login page opens which looks so ugly 😩:

To fix that, I tried to follow this guide at Microsoft learn. But I'm hit with this error (code link here) 😩:

The name 'Engine' does not exist in the current context

Can someone please guide me in the right direction for styling Identity Razor Pages in Blazor to get a consistent look and feel with rest of the app? Microsoft learn guide seems to be lacking.


Solution

  • The documentation totally misses these 2 lines that need to be added to the top of _Layout.cshtml file:

    @using Microsoft.AspNetCore.Mvc.ViewEngines
    @inject ICompositeViewEngine Engine
    

    Adding those lines resolved the error.

    However, I didn't even go that route at the end.


    If you're interested in my complete solution, this is what I ended up doing:

    In summary, I took MainLayout.razor and put it inside _Layout.cshtml.

    If the steps mentioned below are hard to follow, take a look at the code. The full source code is here.

    Demo

    Unauthorized View image

    Login Page image

    Authorized View image

    Step 1:

    Since we're using NavMenu and LoginDisplay razor components inside _Layout.cshtml, add these to the top of _Layout.cshtml:

    @using HMT.Web.Server.Features.Shared.Layout.NavMenu
    @using HMT.Web.Server.Features.Identity
    

    Step 2:

    Replace <body> with this:

    <body>
        <div class="page">
            <div class="sidebar">
                <component type="typeof(NavMenu)" render-mode="ServerPrerendered" />
            </div>
    
            <main>
                <div class="top-row px-4 logindisplay">
                    <component type="typeof(LoginDisplay)" render-mode="ServerPrerendered" />
                </div>
    
                <article class="content px-4">
                    @RenderBody()
                </article>
    
                <div class="bottom-row px-4">
                    <a href="">
                        <img src="/images/logo-black.png" width="40"/>
                        <span class="fs-4">HMT</span>
                    </a>
                </div>
    
                <div id="blazor-error-ui">
                    <environment include="Staging,Production">
                        An error has occurred. This application may no longer respond until reloaded.
                    </environment>
                    <environment include="Development">
                        An unhandled exception has occurred. See browser dev tools for details.
                    </environment>
                    <a href="" class="reload">Reload</a>
                    <a class="dismiss">🗙</a>
                </div>
            </main>
        </div>
    
        <script src="~/Identity/lib/jquery/dist/jquery.js"></script>
        <script src="~/Identity/lib/bootstrap/dist/js/bootstrap.bundle.js"></script>
        <script src="~/Identity/js/site.js" asp-append-version="true"></script>
        @RenderSection("Scripts", required: false)
        <script src="_framework/blazor.server.js"></script>
    </body>
    

    If you're thinking that I just cut and pasted <div class="page"> section from MainLayout.razor, you're absolutely right.😁

    Step 3:

    Create: Features/Shared/Layout/_Layout.cshtml.css file. Now cut and paste all the css styles from your MainLayout.razor.css to _Layout.cshtml.css. For reference, this is how mine looks like.

    Step 4:

    All that remains inside MainLayout.razor is this:

    @inherits LayoutComponentBase
    <PageTitle>Handy Man's Tool</PageTitle>
    @Body
    

    Step 5:

    Wrap AuthorizeView in LoginDisplay.razor with <CascadingAuthenticationState>. The reason is that if you try to use a Razor Component that has Authorize components from component that IS NOT wrapped with <CascadingAuthenticationState>, it won't work, That is the reason why <component type="typeof(LoginDisplay)" render-mode="ServerPrerendered" /> doesn't work in _Layout.cshtml without it.

    Step 6:

    If you want to protect the NavLinks in NavMenu, wrap them inside AuthorizeView, and since NavMenu is rendered from _Layout.cshtml, don't forget to wrap them inside <CascadingAuthenticationState>. For eg: Mine looks like this:

    <CascadingAuthenticationState>
        <div class="@NavMenuCssClass" @onclick="ToggleNavMenu">
            <nav class="flex-column">
                <div class="nav-item px-3">
                    <NavLink class="nav-link" href="" Match="NavLinkMatch.All">
                        <span class="oi oi-home" aria-hidden="true"></span> Home
                    </NavLink>
                </div>
                <AuthorizeView>
                    <div class="nav-item px-3">
                        <NavLink class="nav-link" href="counter">
                            <span class="oi oi-plus" aria-hidden="true"></span> Counter
                        </NavLink>
                    </div>
                    <div class="nav-item px-3">
                        <NavLink class="nav-link" href="fetchdata">
                            <span class="oi oi-list-rich" aria-hidden="true"></span> Fetch data
                        </NavLink>
                    </div>
                </AuthorizeView>
            </nav>
        </div>
    </CascadingAuthenticationState>
    

    Step 7:

    Remove the following files as they're unnecessary:

    1. Areas/Identity/Pages/_ValidationScriptsPartial.cshtml (as I'm not overriding it.)
    2. Features/Shared/Layout/_ViewImports.cshtml
    3. Features/Shared/Layout/_LoginPartial.cshtml (Using LoginDisplay instead)

    Full source code

    https://github.com/akhanalcs/blazor-server-auth/tree/feature/LayoutWithIdentityPages