The error is happening because of the delay between the document loading and Blazor being "ready" to process your request to add a component.
There doesn't appear to be any official documented solution for this, but something like this is possible
Change Blazor to manual start:
index.html
<script src="_framework/blazor.webassembly.js" autostart="false"></script> <script src="js/script.js" defer></script>
Start Blazor, then try to add components - repeat until success
script.js
document.addEventListener("DOMContentLoaded", startBlazor) function startBlazor() { Blazor .start() .then(() => { requestAnimationFrame(AddBlazorComponents) }) .catch((error) => console.error(error)); } let attempts = 0 const maxAttempts = 120 // arbitrary choice function AddBlazorComponents() { if (attempts > maxAttempts) { // give up after trying too many times console.error("Could not load Blazor components.") return } const containerElement = document.querySelector('#app') Blazor.rootComponents.add(containerElement, 'app', {}) .catch(reason => { if (reason.message === "Dynamic root components have not been enabled in this application.") requestAnimationFrame(AddBlazorComponents) else console.error(reason) }) attempts++ }
Better solution?
You could, possibly, add a JSInterop call to your .NET code to facilitate this, after the application starts - but it would be different for the different hosting models : Blazor Server / Blazor WebAssembly.
Even better might be to make a PR to the aspnetcore repo with a method of pre-registering components for the framework to load when it is ready.
Update 29 sep 2021
It turns out there is some currently undocumented functionality that helps here. I have copied my comment from @Swimburger 's github issue below:
The new JavaScript Initializer afterStarted
is called (potentially) too soon for Dynamic components, but it turns out you can pass an initializer to be called for a dynamic component - but it is not well documented or intuitive imho.
To make this work, I changed the app startup like this (adding javaScriptInitializer: "loadApp"
) in program.cs:
builder.RootComponents.RegisterForJavaScript<App>(identifier: "app", javaScriptInitializer: "loadApp");
Then, and this is where it was non-intuitive, I added an export to my module (like afterStarted
) called loadApp
- but it wasn't called.
It turned out that loadApp
is only found if I add it to the window
object, so I added this to index.html
<script src="_framework/blazor.webassembly.js"></script> <script> window.loadApp = function (component,params) { let containerElement = document.querySelector('#app') window.Blazor.rootComponents.add(containerElement, component, params) } </script>
This feels like I am missing something in how to export my custom initializer OR it really does need to be added to window
directly, which seems odd...