Blazor Custom Elements: Using Blazor Components Outside Blazor

What if you could use your Blazor components in a React app, a plain HTML page, or a legacy MVC application? Blazor custom elements make this possible by wrapping Blazor components as standard web custom elements — the browser-native component model that works everywhere.

What Are Custom Elements?

Custom elements are a web standard that lets you define your own HTML tags. A custom element like <my-counter></my-counter> is a real HTML element with its own lifecycle, attributes, and behaviour. Blazor custom elements let you package a Blazor component as one of these elements.

Setting Up

Install the package:

terminal
dotnet add package Microsoft.AspNetCore.Components.CustomElements

Register your Blazor component as a custom element in Program.cs:

Program.cs
builder.Services.AddServerSideBlazor(options =>
{
    options.RootComponents.RegisterCustomElement<Counter>("blazor-counter");
    options.RootComponents.RegisterCustomElement<WeatherWidget>("weather-widget");
});

The first argument is the Blazor component type. The second is the custom element tag name (which must contain a hyphen, per the web standard).

Creating the Component

Your Blazor component doesn't need any special modifications. Standard components work as-is:

Counter.razor
<div class="counter">
    <p>Count: @CurrentCount</p>
    <button @onclick="Increment">Click me</button>
</div>

@code {
    [Parameter]
    public int StartValue { get; set; } = 0;

    private int CurrentCount { get; set; }

    protected override void OnInitialized()
    {
        CurrentCount = StartValue;
    }

    private void Increment()
    {
        CurrentCount++;
    }
}

Using in Plain HTML

Once registered, use it in any HTML page that includes the Blazor script:

page.html
<!DOCTYPE html>
<html>
<head>
    <title>Custom Element Demo</title>
</head>
<body>
    <h1>My Page</h1>

    <!-- Use the Blazor component as a custom element -->
    <blazor-counter start-value="10"></blazor-counter>

    <script src="_framework/blazor.server.js"></script>
</body>
</html>

Notice that the StartValue parameter maps to the start-value HTML attribute. Blazor automatically converts PascalCase parameters to kebab-case attributes.

Passing Parameters

Parameters are passed as HTML attributes. Blazor handles type conversion for primitive types:

page.html
<!-- String parameter -->
<weather-widget city="London"></weather-widget>

<!-- Number parameter -->
<blazor-counter start-value="42"></blazor-counter>

<!-- Boolean parameter -->
<user-card show-email="true"></user-card>

For complex types, you can set parameters from JavaScript:

script.js
const widget = document.querySelector('weather-widget');
widget.setAttribute('city', 'Manchester');

// Or set the property directly for complex types
widget.config = { units: 'metric', refreshInterval: 30 };

Using in MVC or Razor Pages

This is particularly useful for gradually adopting Blazor in existing applications. Add interactive Blazor components to your existing Razor views:

cshtml
@* In a Razor Page or MVC View *@

<h2>Dashboard</h2>

<div class="widget-grid">
    <blazor-counter start-value="0"></blazor-counter>
    <weather-widget city="@Model.UserCity"></weather-widget>
</div>

@section Scripts {
    <script src="_framework/blazor.server.js"></script>
}

This approach lets you incrementally introduce interactivity without rewriting your entire application.

Custom Elements with WebAssembly

For WebAssembly-based custom elements, the setup is slightly different:

Example.cs
// In a Blazor WebAssembly project
builder.RootComponents.RegisterCustomElement<Counter>("blazor-counter");
page.html
<!-- In the host page -->
<blazor-counter start-value="5"></blazor-counter>

<script src="_framework/blazor.webassembly.js"></script>

WebAssembly custom elements are fully client-side — they don't need a server connection after the initial download.

Events and Callbacks

Custom elements can dispatch events back to the hosting page. Use EventCallback in your component and wire up JavaScript event listeners:

NotificationBell.razor
@code {
    [Parameter]
    public EventCallback<int> OnCountChanged { get; set; }

    private int unreadCount;

    private async Task MarkAllRead()
    {
        unreadCount = 0;
        await OnCountChanged.InvokeAsync(unreadCount);
    }
}

From JavaScript:

script.js
const bell = document.querySelector('notification-bell');
bell.addEventListener('oncountchanged', (event) => {
    console.log('Unread count:', event.detail);
    updateBadge(event.detail);
});

Limitations

Custom elements have some constraints worth knowing:

When to Use Custom Elements

Custom elements are ideal for:

They're not a replacement for building a full Blazor application. If you're starting from scratch, use Blazor's native component model. Custom elements are the bridge for when Blazor needs to coexist with other technologies.