aspnetcore/blazor/components/js-spa-frameworks.md
This article covers how to render Razor components from JavaScript, use Blazor custom elements, and generate Angular and React components.
<!-- UPDATE 11.0 - The `blazor.web.js` (Blazor Web App) portions of this article have been commented out for the time being to facilitate reconstituting the guidance later when support lands. The PU work is tracked by https://github.com/dotnet/aspnetcore/issues/53920. -->[!NOTE] We recommend using the
blazor.server.js(Blazor Server) andblazor.webassembly.js(Blazor WebAssembly) scripts when integrating Razor components into an existing JavaScript app until better support for theblazor.web.js(Blazor Web App) script is added in the future. For more information, see RegisterCustomElement stopped working in Blazor 8 (dotnet/aspnetcore#53920).
The following sample apps demonstrate rendering a Razor component as a Blazor custom element in an Angular app:
javiercn/CustomElementsBlazorSample GitHub repository, branch: blazor-server)javiercn/CustomElementsBlazorSample GitHub repository, branch: blazor-wasm)To migrate either of these .NET 7 samples, see the following resources:
The principal updates to make are:
Update the target framework monikers (TFMs) to the latest version.
Update the .NET package references and Angular dependencies to their latest versions.
Razor components can be dynamically-rendered from JavaScript (JS) for existing JS apps.
The example in this section renders the following Razor component into a page via JS.
Quote.razor:
<div class="m-5 p-5">
<h2>Quote</h2>
<p>@Text</p>
</div>
@code {
[Parameter]
public string? Text { get; set; }
}
In the Program file, add the namespace for the location of the component.
Call xref:Microsoft.AspNetCore.Components.Web.JSComponentConfigurationExtensions.RegisterForJavaScript%2A on the app's root component collection to register a Razor component as a root component for JS rendering.
xref:Microsoft.AspNetCore.Components.Web.JSComponentConfigurationExtensions.RegisterForJavaScript%2A includes an overload that accepts the name of a JS function that executes initialization logic (javaScriptInitializer). The JS function is called once per component registration immediately after the Blazor app starts and before any components are rendered. This function can be used for integration with JS technologies, such as HTML custom elements or a JS-based SPA framework.
One or more initializer functions can be created and called by different component registrations. The typical use case is to reuse the same initializer function for multiple components, which is expected if the initializer function is configuring integration with custom elements or another JS-based SPA framework.
[!IMPORTANT] Don't confuse the
javaScriptInitializerparameter of xref:Microsoft.AspNetCore.Components.Web.JSComponentConfigurationExtensions.RegisterForJavaScript%2A with JavaScript initializers. The name of the parameter and the JS initializers feature is coincidental.
The following example demonstrates the dynamic registration of the preceding Quote component with "quote" as the identifier.
In a Blazor Server app, modify the call to xref:Microsoft.Extensions.DependencyInjection.ComponentServiceCollectionExtensions.AddServerSideBlazor%2A in the Program file:
builder.Services.AddServerSideBlazor(options =>
{
options.RootComponents.RegisterForJavaScript<Quote>(identifier: "quote",
javaScriptInitializer: "initializeComponent");
});
In a Blazor WebAssembly app, call xref:Microsoft.AspNetCore.Components.Web.JSComponentConfigurationExtensions.RegisterForJavaScript%2A on xref:Microsoft.AspNetCore.Components.WebAssembly.Hosting.WebAssemblyHostBuilder.RootComponents in the client-side Program file:
builder.RootComponents.RegisterForJavaScript<Quote>(identifier: "quote",
javaScriptInitializer: "initializeComponent");
Attach the initializer function with name and parameters function parameters to the window object. For demonstration purposes, the following initializeComponent function logs the name and parameters of the registered component.
wwwroot/jsComponentInitializers.js:
window.initializeComponent = (name, parameters) => {
console.log({ name: name, parameters: parameters });
}
Render the component from JS into a container element using the registered identifier, passing component parameters as needed.
In the following example:
Quote component (quote identifier) is rendered into the quoteContainer element when the showQuote function is called.Text parameter.wwwroot/scripts.js:
window.showQuote = async () => {
let targetElement = document.getElementById('quoteContainer');
await Blazor.rootComponents.add(targetElement, 'quote',
{
text: "Crow: I have my doubts that this movie is actually 'starring' " +
"anybody. More like, 'camera is generally pointed at.'"
});
}
const btn = document.querySelector("#showQuoteBtn");
btn.addEventListener("click", showQuote);
After the Blazor script is loaded, load the preceding scripts into the JS app:
<script src="_framework/{BLAZOR SCRIPT}"></script>
<script src="jsComponentInitializers.js"></script>
<script src="scripts.js"></script>
In the preceding example, the {BLAZOR SCRIPT} placeholder is the Blazor script.
In HTML, place the target container element (quoteContainer). For the demonstration in this section, a button triggers rendering the Quote component by calling the showQuote JS function:
<button id="showQuoteBtn">Show Quote</button>
<div id="quoteContainer"></div>
On initialization before any components are rendered, the browser's developer tools console logs the Quote component's identifier (name) and parameters (parameters) when initializeComponent is called:
Object { name: "quote", parameters: (1) […] }
name: "quote"
parameters: Array [ {…} ]
0: Object { name: "Text", type: "string" }
length: 1
When the :::no-loc text="Show Quote"::: button is selected, the Quote component is rendered with the quote stored in Text displayed:
Quote ©1988-1999 Satellite of Love LLC: Mystery Science Theater 3000 (Trace Beaulieu (Crow))
[!NOTE]
rootComponents.addreturns an instance of the component. Calldisposeon the instance to release it:javascriptconst rootComponent = await window.Blazor.rootComponents.add(...); ... rootComponent.dispose();
The preceding example dynamically renders the root component when the showQuote() JS function is called. To render a root component into a container element when Blazor starts, use a JavaScript initializer to render the component, as the following example demonstrates.
The following example builds on the preceding example, using the Quote component, the root component registration in the Program file, and the initialization of jsComponentInitializers.js. The showQuote() function (and the script.js file) aren't used.
In HTML, place the target container element, quoteContainer2 for this example:
<div id="quoteContainer2"></div>
Using a JavaScript initializer, add the root component to the target container element.
wwwroot/{PACKAGE ID/ASSEMBLY NAME}.lib.module.js:
export function afterStarted(blazor) {
let targetElement = document.getElementById('quoteContainer2');
blazor.rootComponents.add(targetElement, 'quote',
{
text: "Crow: I have my doubts that this movie is actually 'starring' " +
"anybody. More like, 'camera is generally pointed at.'"
});
}
[!NOTE] For the call to
rootComponents.add, use theblazorparameter (lowercaseb) provided by the Blazor start event. Although the registration is valid when using theBlazorobject (uppercaseB), the preferred approach is to use the parameter.
For an advanced example with additional features, see the example in the BasicTestApp of the ASP.NET Core reference source (dotnet/aspnetcore GitHub repository):
:::moniker range=">= aspnetcore-7.0"
Use Blazor custom elements to dynamically render Razor components from different JavaScript technologies, such as Angular, React, and Vue.
Blazor custom elements:
Custom elements don't support child content or templated components.
Per the HTML specification, custom element tag names must adopt kebab case:
<span aria-hidden="true">❌</span><span class="visually-hidden">Invalid:</span> mycounter
<span aria-hidden="true">❌</span><span class="visually-hidden">Invalid:</span> MY-COUNTER
<span aria-hidden="true">❌</span><span class="visually-hidden">Invalid:</span> MyCounter
<span aria-hidden="true">✔️</span><span class="visually-hidden">Valid:</span> my-counter
<span aria-hidden="true">✔️</span><span class="visually-hidden">Valid:</span> my-cool-counter
Add a package reference for Microsoft.AspNetCore.Components.CustomElements to the app's project file.
The following examples are based on the Counter component from the Blazor project template.
Counter.razor:
:::code language="razor" source="~/../blazor-samples/8.0/BlazorSample_BlazorWebApp/Components/Pages/Counter.razor":::
:::moniker-end
<!-- HOLD :::moniker range=">= aspnetcore-8.0" ### Blazor Web App registration Take the following steps to register a root component as a custom element in a Blazor Web App. Add the <xref:Microsoft.AspNetCore.Components.Web?displayProperty=fullName> namespace to the top of the server-side `Program` file: ```csharp using Microsoft.AspNetCore.Components.Web; ``` Add a namespace for the app's components. In the following example, the app's namespace is `BlazorSample` and the components are located in the `Components/Pages` folder: ```csharp using BlazorSample.Components.Pages; ``` Modify the call to <xref:Microsoft.Extensions.DependencyInjection.ServerRazorComponentsBuilderExtensions.AddInteractiveServerComponents%2A> to specify the custom element with <xref:Microsoft.AspNetCore.Components.Web.CustomElementsJSComponentConfigurationExtensions.RegisterCustomElement%2A> on the <xref:Microsoft.AspNetCore.Components.Server.CircuitOptions.RootComponents> circuit option. The following example registers the `Counter` component with the custom HTML element `my-counter`: ```csharp builder.Services.AddRazorComponents() .AddInteractiveServerComponents(options => { options.RootComponents.RegisterCustomElement<Counter>("my-counter"); }); ``` :::moniker-end -->:::moniker range=">= aspnetcore-7.0"
Take the following steps to register a root component as a custom element in a Blazor Server app.
Add the xref:Microsoft.AspNetCore.Components.Web?displayProperty=fullName namespace to the top of the Program file:
using Microsoft.AspNetCore.Components.Web;
Add a namespace for the app's components. In the following example, the app's namespace is BlazorSample and the components are located in the Pages folder:
using BlazorSample.Pages;
Modify the call to xref:Microsoft.Extensions.DependencyInjection.ComponentServiceCollectionExtensions.AddServerSideBlazor%2A. Specify the custom element with xref:Microsoft.AspNetCore.Components.Web.CustomElementsJSComponentConfigurationExtensions.RegisterCustomElement%2A on the xref:Microsoft.AspNetCore.Components.Server.CircuitOptions.RootComponents circuit option. The following example registers the Counter component with the custom HTML element my-counter:
builder.Services.AddServerSideBlazor(options =>
{
options.RootComponents.RegisterCustomElement<Counter>("my-counter");
});
Register a root component as a custom element in a Blazor WebAssembly app. In the following example, the code:
BlazorSample, and the components are located in the Pages folder.Counter component with the custom HTML element my-counter.using BlazorSample.Pages;
using Microsoft.AspNetCore.Components.Web;
using Microsoft.AspNetCore.Components.WebAssembly.Hosting;
var builder = WebAssemblyHostBuilder.CreateDefault(args);
builder.RootComponents.RegisterCustomElement<Counter>("my-counter");
await builder.Build().RunAsync();
Use the custom element with any web framework. For example, the preceding my-counter custom HTML element that renders the app's Counter component is used in a React app with the following markup:
<my-counter></my-counter>
For a complete example of how to create custom elements with Blazor, see the CustomElementsComponent component in the reference source.
Pass parameters to your Razor component either as HTML attributes or as JavaScript properties on the DOM element.
The following Counter component uses an IncrementAmount parameter to set the increment amount of the :::no-loc text="Click me"::: button.
Counter.razor:
<h1>Counter</h1>
<p role="status">Current count: @currentCount</p>
<button class="btn btn-primary" @onclick="IncrementCount">Click me</button>
@code {
private int currentCount = 0;
[Parameter]
public int IncrementAmount { get; set; } = 1;
private void IncrementCount()
{
currentCount += IncrementAmount;
}
}
Render the Counter component with the custom element and pass a value to the IncrementAmount parameter as an HTML attribute. The attribute name adopts kebab-case syntax (increment-amount, not IncrementAmount):
<my-counter increment-amount="10"></my-counter>
Alternatively, you can set the parameter's value as a JavaScript property on the element object. The property name adopts camel case syntax (incrementAmount, not IncrementAmount):
const elem = document.querySelector("my-counter");
elem.incrementAmount = 10;
You can update parameter values at any time using either attribute or property syntax.
Supported parameter types:
:::moniker-end
:::moniker range="< aspnetcore-7.0"
Experimental support is available for building custom elements using the Microsoft.AspNetCore.Components.CustomElements NuGet package. Custom elements use standard HTML interfaces to implement custom HTML elements.
[!WARNING] Experimental features are provided for the purpose of exploring feature viability and may not ship in a stable version.
Register a root component as a custom element:
In a Blazor Server app, modify the call to xref:Microsoft.Extensions.DependencyInjection.ComponentServiceCollectionExtensions.AddServerSideBlazor%2A in the Program file to call xref:Microsoft.AspNetCore.Components.Web.CustomElementsJSComponentConfigurationExtensions.RegisterCustomElement%2A on xref:Microsoft.AspNetCore.Components.Server.CircuitOptions.RootComponents%2A?displayProperty=nameWithType:
builder.Services.AddServerSideBlazor(options =>
{
options.RootComponents.RegisterCustomElement<Counter>("my-counter");
});
[!NOTE] The preceding code example requires a namespace for the app's components (for example,
using BlazorSample.Components.Pages;) in theProgramfile.
In a Blazor WebAssembly app, call xref:Microsoft.AspNetCore.Components.Web.CustomElementsJSComponentConfigurationExtensions.RegisterCustomElement%2A on xref:Microsoft.AspNetCore.Components.WebAssembly.Hosting.WebAssemblyHostBuilder.RootComponents?displayProperty=nameWithType in the Program file:
builder.RootComponents.RegisterCustomElement<Counter>("my-counter");
[!NOTE] The preceding code example requires a namespace for the app's components (for example,
using BlazorSample.Components.Pages;) in theProgramfile.
Include the following <script> tag in the app's HTML before the Blazor script tag:
<script src="/_content/Microsoft.AspNetCore.Components.CustomElements/BlazorCustomElements.js"></script>
Use the custom element with any web framework. For example, the preceding counter custom element is used in a React app with the following markup:
<my-counter increment-amount={incrementAmount}></my-counter>
[!WARNING] The custom elements feature is currently experimental, unsupported, and subject to change or be removed at any time. We welcome your feedback on how well this particular approach meets your requirements.
:::moniker-end
Generate JavaScript (JS) components from Razor components for JavaScript technologies, such as Angular or React. This capability isn't included with .NET, but is enabled by the support for rendering Razor components from JS. The JS component generation sample on GitHub demonstrates how to generate Angular and React components from Razor components. See the GitHub sample app's README.md file for additional information.
[!WARNING] The Angular and React component features are currently experimental, unsupported, and subject to change or be removed at any time. We welcome your feedback on how well this particular approach meets your requirements.