Telerik blogs

Now you can embed Blazor components anywhere you can run JavaScript, including on sites that use a JavaScript framework like React or Angular.

Blazor has evolved into a productive, stable and reliable framework for building web applications.

For C# developers especially it’s opened up a new frontier, enabling us to bring our existing knowledge and skills to the world of “modern web development.”

Until recently, if you wanted to use Blazor, you pretty much had to adopt it in its entirety—building a new Blazor site from scratch.

But what if you have existing JavaScript code (and expertise on your teams)? You may well need to take what you’ve already got and extend it with Blazor, perhaps using it for distinct areas of your site, or on specific pages where Blazor seems like a good fit.

With .NET 7 this becomes much easier, thanks to official support for HTML Custom Elements.

What’s a Custom Element?

Custom elements are part of the HTML spec. They make it possible for the HTML parser (running in your browser) to handle “non-standard” elements.

For example, you could create a Greeting component, then use a little bit of JavaScript to register it as an HTMLElement.

class Greeting extends HtmlElement { 
    
}

customElements.define("my-greeting", Greeting);

With this, when you declare an instance of my-greeting on a page, the HTML parser will construct a new instance of the Greeting class:

<my-greeting></my-greeting>

So how does this relate to Blazor?

Embed Blazor Components Anywhere

As of .NET 7, Blazor components can be registered as Custom Elements. This means you can render certain types of Blazor components anywhere you can run JavaScript.

But how does it work in practice?

Take this simple Greeting Blazor component:

Greeting.razor

<h1>
    Hello @Name
</h1>
@code {
    
    [Parameter]
    public string Name { get;set; } = "Alice";
    
}

This will greet whoever’s name is passed in via the Name parameter—for example:

<Greeting Name="Samantha" />

Now this will work just fine in a Blazor application, but what if you want to use it with a regular HTML and JavaScript site?

To register your component as a custom element, you need to:

  • Define the component in your Blazor application
  • Register the component as a custom element (with a unique tag identifier)
  • Reference the Blazor runtime on an HTML page
  • Render an instance of the Greeting component (using its custom tag identifier) on said HTML page

First you’ll need a Blazor application, which contains the component you want to use (Greeting in this case).

You’ll also need to reference the official Custom Elements Nuget package:

dotnet add package Microsoft.AspNetCore.Components.CustomElements

Then, in your web application’s Program.cs you can register the custom element.

In Blazor WASM:

public static async Task Main(string[] args)
{
    var builder = WebAssemblyHostBuilder.CreateDefault(args);
  
    ...

    // register the Greeting component as a custom element
    builder.RootComponents.RegisterCustomElement<Greeting>("my-greeting");

    await builder.Build().RunAsync();
}

Or Blazor Server:

builder.Services.AddServerSideBlazor(options =>
{
    options.RootComponents.RegisterCustomElement<Greeting>("my-greeting");
});

You can now use your Greeting component, via its custom tag identifier (my-greeting) on any page.

Let’s start by testing it on the default “home” page for our Blazor app. If you’re using WASM, it will be Index.html; for Blazor Server, you’re looking for _Host.cshtml.

Somewhere in the body of that default page, declare an instance of the Greeting component using its custom element tag (my-greeting).

Index.html (or _Host.cshtml)

...

<body>
    
    <my-greeting name="Jeff"></my-greeting>
    
    ...
    
</body>

Now before you spend hours trying to work out why your custom element isn’t working, there’s one important detail about custom elements …

The Custom Elements spec requires that all custom elements contain a hyphen (-) in the tag name.

This caught me out the first time I tried to use the name greeting for this custom element.

Specifically your custom element tag names must adopt kebab case. For example, these tag names won’t work:

  • mygreeting
  • MY-GREETING
  • MyGreeting

While these will work:

  • my-greeting
  • my-better-greeting

With my-greeting successfully registered as a custom element, and an instance declared on your index.html page, you now have a Blazor component rendering on a standard HTML page.

This is all well and good, but so far we’ve only really done something we could have achieved much more easily by using our Greeting component directly within our Blazor app.

How can we use custom elements to render Blazor components elsewhere, on standard HTML pages and via JavaScript?

The answer to that depends on whether you’re using Blazor Server or Blazor WASM.

Rendering Custom Elements via Blazor Server

If you’re using Blazor Server, you’ll need to continue to use an ASP.NET Core app as your host. That makes sense given how Blazor Server holds component state in memory on the server itself.

The easiest way to render a custom element on a standalone HTML page is to create an html page in the wwwroot folder and directly reference the custom element there:

<!DOCTYPE html>
<html lang="en">
<head>
    <base href="/"/>
    <script src="_framework/blazor.server.js"></script>
</head>
<body>
    <my-greeting name="Indy"></my-greeting>
</body>
</html>

The <base href="/" is important—without it your server will fail to load the relevant Blazor files.

We’ve referenced the core Blazor Server JavaScript file (the glue that holds this together, by starting Blazor and enabling the link between Blazor and the browser’s DOM), then rendered an instance of our greeting component in the main body of the page.

To test this, launch your Blazor Server application, then manually navigate to /demo.html.

The ASP.NET Core server will serve the demo.html page, which, in turn, will connect to Blazor Server (using the script from blazor.server.js) and then render the my-greeting custom element, rendering it as standard HTML.

If you inspect the source code for the rendered component using your browser’s DevTools, you’ll see something like this:

<my-greeting name="Indy">
    <h1>
    	Hello Indy
    </h1>
</my-greeting>

Now you might notice, when you inspect your component in the browser, that it’s missing styles. To get those, you need to reference the same CSS you’re using in your Blazor Server project.

<!DOCTYPE html>
<html lang="en">
<head>
    <base href="/"/>
    
    <!-- link to any relevant stylesheets -->
    <link rel="stylesheet" href="Your-Project.styles.css">
    <link rel="stylesheet" href="css/bootstrap/bootstrap.min.css">
    <link rel="stylesheet" href="css/app.css">  
    
    <script src="_framework/blazor.server.js"></script>
</head>
<body>
    <my-greeting name="Indy"></my-greeting>
</body>
</html>

But what if you want to break free of ASP.NET Core entirely, and embed your Blazor components somewhere else, on a page which isn’t hosted via ASP.NET?

For that you can turn to Blazor WASM.

Render Blazor Components Anywhere via Blazor WASM

To serve up your Blazor custom elements from virtually any HTML page (or JavaScript app), you’ll need to use Blazor WASM.

First you need to get hold of the Blazor components, and the Blazor framework itself, so you can reference it from a standard HTML page.

To do that, you’ll need to publish your Blazor app, via your IDE, or the dotnet command:

dotnet publish

When you locate the published app (by default it will be in the Debug folder), you’ll find two all important folders: _content and _framework.

Directory tree showing a .NET

  • _framework contains the Blazor runtime, which you need to make all this work.
  • _content includes the relevant JavaScript script from Microsoft’s Custom Elements Nuget package.

There are other folders, like css, that you’ll want to bring across to ensure your components look right in their new home.

Start by copying the following to a new folder:

  • _content
  • _framework
  • css
  • Your-Project.styles.css (BlazorExamples.WASM.styles.css in the example above)

Once you’ve copied those, in the same folder, create a “blank” index.html file.

The resulting folder should look something like this:

Directory showing

Now replace the contents of the index.html file with this:

<html>
<head>
    <base href="/"/>
    <script src="_framework/blazor.webassembly.js"></script>
</head>
<body>

    <my-greeting name="Susan"></my-greeting>

</body>
</html>

That <base href="/" line is important—without that your custom elements won’t load.

From here you need to launch a local web development server to test your work (it’s not enough to just open index.html in the browser).

If you want a quick option (and don’t mind letting a little Node.js into your life!), you can issue this command (assuming you have Node installed on your machine):

 npm install http-server -g

Then, from your folder (the one we just created, with index.html), issue this command:

http-server

You should now see output from http-server indicating the local addresses where your site can be accessed:

Connection Timeout: 120 seconds
Directory Listings: visible
AutoIndex: visible
Serve GZIP Files: false
Serve Brotli Files: false
Default File Extension: none

Available on:
  http://192.168.1.31:8081
  http://127.0.0.1:8081
  http://172.31.32.1:8081
Hit CTRL-C to stop the server

Hit one of those addresses, and you should see your Blazor greeting component in all its glory:

A browser page showing the words

Admittedly, it doesn’t look great. That’s because we haven’t (yet) told the HTML page to use any of the styles from the original Blazor app.

Let’s add links to the relevant CSS files from the folder you copied over earlier.

<html>
<head>
    <base href="/"/>
    <script src="_framework/blazor.webassembly.js"></script>
    
    <!-- link to any relevant stylesheets -->
    <link rel="stylesheet" href="Your-Project.styles.css">
    <link rel="stylesheet" href="css/bootstrap/bootstrap.min.css">
    <link rel="stylesheet" href="css/app.css">    
</head>
<body>

    <my-greeting name="Susan"></my-greeting>

</body>
</html>

Preview this in the browser now and you should see a fully styled greeting:

A browser page showing the words

Interact with Your Blazor Custom Elements via JavaScript

We’ve seen how to declare an instance of my-greeting in HTML:

<my-greeting name="Alice"></my-greeting>

You can also interact with a custom element via JavaScript—useful for updating its parameters in response to other events:

const elem = document.querySelector("my-greeting");
elem.name = "Jeff";

This brings an added benefit in that more parameter types are supported when you interact with the element via JavaScript than when you pass values as HTML attributes.

Using JavaScript, you can pass any object which is JSON serializable.

Using HTML attributes, you’re limited to strings, booleans or numerical types.

Finally, if you opt to interact with your custom elements via JavaScript, you can also attach JavaScript functions to your component’s EventCallback parameters.

Say, for example, you extended the Greeting component to raise an EventCallback when a button is clicked.

Greeting.razor

<h1>
    Hello @Name
</h1>

<button @onclick="OnClicked">Click me!</button>
@code {
    
    [Parameter]
    public string Name { get;set; } = "Alice";

    [Parameter]
    public EventCallback OnClicked { get; set; }
    
}

You can attach a JavaScript function to the OnClicked callback parameter.

const greeting = document.querySelector("my-greeting");

greeting.onClicked = () => {
	console.log("clicked!")
};

One thing to watch here: Blazor usually starts automatically when you reference the script:

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

To attach a JavaScript handler, you need to do so at the “right” time—after Blazor has finished loading.

You can achieve this by disabling Blazor’s (default) autostart behavior, and starting it yourself instead.

<script src="_framework/blazor.server.js" autostart="false"></script>

<script>
    const greeting = document.querySelector("my-greeting");

    Blazor.start().then(() => {
        greeting.onClicked = () => {
            console.log("clicked!")
        };
    });
</script>

With this, any button clicks in the my-greeting component will trigger the JavaScript function, which will log the string “clicked!” to the browser’s console.

In Summary

Blazor’s official support for Custom Elements makes it possible to render, and interact with, Blazor components anywhere you can run JavaScript.

This is especially useful if you want to use Blazor in an existing HTML/JavaScript site and/or want to adopt Blazor incrementally.

The types of parameters you can send via HTML attributes is limited to primitives (bools, strings and numerical types), but if you interact with the component via JavaScript you can pass any JavaScript serializable object.

Finally, if you want to react to events in your component, you can attach JavaScript functions to EventCallback parameters.

Telerik UI for Blazor	Truly Native Blazor UI Components


Jon Hilton
About the Author

Jon Hilton

Jon spends his days building applications using Microsoft technologies (plus, whisper it quietly, a little bit of JavaScript) and his spare time helping developers level up their skills and knowledge via his blog, courses and books. He's especially passionate about enabling developers to build better web applications by mastering the tools available to them. Follow him on Twitter here.

Related Posts

Comments

Comments are disabled in preview mode.