.netblazorbootstrap-5blazor-webassembly

How to stop Blazor from reloading page and clearing html tag?


I'm using .NET 8 Blazor WASM (Server and Client projects) and Bootstrap 5.3. When the page loads I have a javascript file that sets the data-bs-theme class for light/dark mode set in the <html> tag. This can also be changed in my Navbar menu with buttons. However, when I manually route to the example route pages such as /weather and /counter the theme stays except for when I use an <a> tag or .NET's <NavLink> it reloads the entire page and the "data-bs-theme" class is erased leaving just <html lang="en">. But, if I refresh the page after this, the theme is reapplied and works as intended. My NavMenu component is set as @rendermode InteractiveServer.

(function () {
    var theme = localStorage.getItem('theme');

    if (!theme) {
        theme = window.matchMedia('(prefers-color-scheme: dark)').matches ? 'dark' : 'light';;
    }

    document.documentElement.setAttribute('data-bs-theme', theme);
})();

I have tried setting the theme using OnAfterRenderAsync but that doesn't change anything and also causes the page to flash on refresh/first load. I've tried taking off InteractiveServer which doesn't help, the C# code is minimal and the project is nearly identical to a brand new Blazor web app. I'm brand new to Blazor so I'm sure I'm missing something but the Microsoft docs have not mentioned anything about the entire page reloading or editing the tag.

This is addressed similarly here. However, these are changes I'm doing on the root level changing the <html> tag directly. Therefore after some digging I cannot use OnAfterRenderAsync in the app.razor file nor can I invoke JSRuntime. So I'm totally lost as to how I'm supposed to have the tag data-bs-theme persist between page loads as Blazor reloads the entire DOM and does something to erase the attribute I've set. I've tried doing this in MainLayhout, App.Razor, and my own NavMenu Component that I call in MainLayout. This is also addressed in this post but does not have an answer and one suggested answer seems over-engineered. I'm new to Blazor and if this post shows the correct way to do this as I've tried it and cannot get it working or if there is a better way any help would be greatly appreciated, I am very much stuck on this.


Solution

  • Blazor's Enhanced Navigation

    This solution is specific to executing JavaScript in App.razor in Blazor .NET +8. See this alternative solution if you need to run scripts in a Blazor component.

    The Bootstrap Color Mode (theme) setting script executes as expected during full page load. However, Blazor uses enhanced navigation to minimize load times when navigating to another page. This is a partial load where the original layout is merged with new page content. Changes to the theme attribute are lost during this merge.

    One way to fix this problem is to run the theme setting script on Blazor's enhancedload event which happens after navigation. The listener should be wrapped in a DOMContentLoaded event to ensure that it runs after Blazor has started.

    App.razor

    Example JavaScript to update the Bootstrap theme attribute on both page load and enhanced navigation.

    <script>
    document.addEventListener('DOMContentLoaded', (e) => {
    
        // update on initial page load
        updateTheme();
    
        // then update on partial page reloads
        Blazor.addEventListener('enhancedload', () => {
            updateTheme();
        });
    })
    
    function updateTheme() {
        
        let theme = localStorage.getItem('theme');
    
        if (!/(light|dark)/.test(theme)) {
            theme = window.matchMedia('(prefers-color-scheme: dark)').matches ? 'dark' : 'light';;
            localStorage.setItem('theme', theme);
        }
    
        document.documentElement.setAttribute('data-bs-theme', theme);
    }
    </script>