DEV Community

Cover image for Intro to JS Interop in Blazor
Rasheed K Mozaffar
Rasheed K Mozaffar

Posted on

Intro to JS Interop in Blazor

Hi There! πŸ‘‹πŸ»

If you're a .NET developer, then you already know how amazing it is, to be able to create interactive web UIs and client side applications, using only C#, without any need for JavaScript. Now that Blazor is very mature, with a large community, you're no longer required to play with JS very often (if any) to get things done, thanks to 3rd party NuGet packages, and all the updates to the framework itself.

Introduction

JavaScript has been used in building websites for over 2 decades, it's been the cornerstone for web development, and to this day, it's one of the most widely used programming languages out there. There's also abundance of libraries that encapsulate useful functionality, and they take away the burden of implementing them manually, and many of them are built using JS, and if you want to use them in a Blazor application, you can't just have a script file, a link to it in the HTML, and it'd run like in a normal website.

However, JS Interop has been there since day 1 in Blazor, and if you want to integrate some JS code in your Blazor applications, you have to know how to use this feature.

Setting up the scene πŸ› οΈ

To demonstrate this concept and cover it in depth, we need to get practical, and write some code to see things in action, so for that, we'll use an empty Blazor WebAssembly application.

πŸ’‘ I encourage you to follow and write the code along, because you can use them as future references in case you want to brush up on the concept.

In a folder, run this command:


dotnet new blazorwasm-empty -n JsInteropDemo

Enter fullscreen mode Exit fullscreen mode

This will create a new Blazor Wasm Empty project under the name JsInteropDemo. Now open the project in your preferred text editor, and let's get going!

How does Js Interop work? πŸ€”

In essence, JS Interop allows bidirectional communication between C# and JavaScript, in other words, it allows C# code to invoke JS functions, and JS functions to call C# code. We'll start by looking at calling JS methods from C# code, which is the more common approach, and then explore the opposite.

Let's call our first JS method πŸ“ž

We'll kick things off with a basic example, to begin with, create a folder called js inside wwwroot, because we like to keep things clean and organized, inside it, create a new js file, name it whatever you want, in my case, I called it app.js, for now, we'll use this basic function:


function sayHello(name) {
    alert(`Hello ${name}`);
}

Enter fullscreen mode Exit fullscreen mode

This will be sufficient to demonstrate how the process of calling a JavaScript function works in C#.
Now, in index.html (_Host.cshtml in Blazor Server), add this line:

    ...
    <script src="js/app.js"></script> // add this
    <script src="_framework/blazor.webassembly.js"></script>
</body>

Enter fullscreen mode Exit fullscreen mode

This line adds a reference to our Js file, now we are ready to test out this function using JS Interop in our razor components.

In Index.razor, below the @page directive, inject an instance of IJSRuntime and give it a name, like this:


@page "/"
@inject IJSRuntime Js

Enter fullscreen mode Exit fullscreen mode

Before we proceed, let's quickly talk about the IJSRuntime interface.
This interface has two methods

  1. InvokeAsync<TValue>
  2. InvokeVoidAsync

In short, the first method is used to invoke JavaScript functions that return a value, the generic parameter is used to map the returned value, into a C# type through JSON Serialization and Deserialization.
The second method is used for functions that don't return anything, aka void methods in C#.

Both methods share a number of arguments, the first being identifier, which is essentially the name of the JS function you're trying to invoke. The second common argument, is an array of objects called args, this is used to supply the function with its parameters, in our case, we have a parameter called name in our JS function, so we'd have to pass that a value when invoking our method.

And lastly, timeout of type TimeSpan, this is used when you want the call to a function to timeout after a given time span, providing any value for it will override the default value.

Now, let's invoke our method..
In the code directory of Index.razor, we'll use OnInitializedAsync, to invoke the sayHello function, like the following:


@code {
    protected override async Task OnInitializedAsync()
    {
        await Js.InvokeVoidAsync("sayHello", args: "Bob");
    }
}

Enter fullscreen mode Exit fullscreen mode

Save the changes, and now run the project, when the home page first loads, you should see something like this:
Alert popup saying Hello Bob

This shows that the JS function got called correctly from our C# code, now let's test out something with a return value.

We'll use the prompt function to get the name of the user and then have the function return whatever the user typed in, and display that in a heading on the page.


function getMessage() {
    var message = prompt("Type in your message");
    return message;
}

Enter fullscreen mode Exit fullscreen mode

In Index.razor, comment out the invocation of sayHello, and replace it with this:


@page "/"
@inject IJSRuntime Js

<h1>Hello, world!</h1>
<h2>@message</h2>

@code {
    private string? message;

    protected override async Task OnInitializedAsync()
    {
        message = await Js.InvokeAsync<string>("getMessage");
    }
}

Enter fullscreen mode Exit fullscreen mode

When the page loads, the site will prompt the user to type in something, and anything entered, will be rendered as an h2, like this:
The result of calling a js function with a return value

πŸ’‘ The JS function I used is rather simple, and we know for a fact it returns a string, thus the mapping was not complex, we just passed string as a type parameter to the InvokeAsync<TValue> method. However, if a JS function returns an object, with like a bunch of properties, you can create a C# class or record and use it as a type parameter, and through JSON Deserialization, the returned object will be mapped into a strongly typed C# object.

Practical use cases for JS Interop

Since JS is a dominating language when it comes to web development, and has a large community, many libraries for various use cases already exist and can be easily integrated. You do have the ability to reinvent the wheel, and build them from scratch using C# and Blazor, but there's no point in doing so, that's when JS Inteop shines.

Libraries for things like Charts, Animations, or pre built, interactive components can be easily integrated in your Blazor applications through JS Interop, and you now know how to call Js code from C#, you are capable of integrating any library you want in your apps.

πŸ’‘ Stay tuned for a future article where I'll demonstrate how to integrate a couple of interesting JS Libraries, in a Blazor application.

Calling C# code from JavaScript

We've looked at how to call JS functions from C# code, now we'll take a quick look on the other side of the equation, which is calling C# methods, from inside JavaScript.
For that, there're two different ways:

  1. Calling static C# methods.
  2. Calling C# instance methods.

πŸ’‘ To keep this article simple and more of an introductory to the concept, I'll only demonstrate calling static C# methods, due to its simplicity, the other way is more sophisticated and will make the article quite lengthy, so for the sake of simplicity, we'll wrap it up with one technique only.

Calling static C# methods from JavaScript

This form is the very straightforward and easy to implement. We'll start by creating a class called ConsoleLogger, inside it, there'll be one static method, like this:


using Microsoft.JSInterop;

namespace JsInteropDemo;

public class ConsoleLogger
{
    [JSInvokable(identifier: "logMessage")]
    public static void LogMessage(string message)
    {
        Console.WriteLine("Called from JavaScript:");
        Console.WriteLine(message);
    }
}

Enter fullscreen mode Exit fullscreen mode

This is all normal C# code, but one thing stands out, it's that [JSInvokable] attribute. This attribute allows JavaScript to call that method (LogMessage) using its name, but in case you want to alias it, the attribute takes a string parameter named identifier, which makes the method invocable through its alias name. For me, I'll change it to logMessage because JavaScript uses Camel Case for naming functions, and I want to keep it consistent.

Now, in app.js, add this code:


async function invokeLogMessageInCsharp() {
    await DotNet.invokeMethodAsync
    ('JsInteropDemo', 'logMessage', 'LogMessage in ConsoleLogger was invoked from JavaScript')
    .catch(error => 
    {
        console.log(error)
    });
}

Enter fullscreen mode Exit fullscreen mode

Let's look through what's going on here:
I'm creating an async function named invokeLogMessageInCsharp, this function awaits a function call on a bizarre object named DotNet, this object is built-in inside Blazor JS, with two functions:

  1. invokeMethod
  2. invokeMethodAsync

The non-async version (synchronous), is only supported in Blazor WebAssembly, and for that reason, you should alwaysΒ go with the async variant so that your code will still work just fine in case you switched over to Blazor Server.

When calling invokeMethodAsync, we have to pass some arguments to it, the first being the assembly inside of which resides the class containing our C# method that we want to call from JavaScript, the second is the identifier, which we specified in the [JSInvokable] attribute, and lastly, the arguments for the method we want to call, since our method anticipates a string, we have to pass that over.

After the call, we're attempting to catch any errors in case something goes wrong, if so, the error will be logged to the browser's console.

Now if you call this function anywhere in the application, you should be able to see the following messages being printed out to the browser's console:
The result of calling a C# static method from JavaScript

Conclusion πŸ“œ

We had quite a long journey in this article, we discovered around the concept of JS Interop, how we can use the IJSRuntime interface, the methods it provides, how to use each one of them, and lastly we looked at how we can call C# static methods from inside JavaScript functions. The aim of this article wasn't to build something fancy, but instead to give you a head start on the concept so that you can start using it in your own projects.

I encourage you to experiment around, give the documentations a go and try looking up how to integrate some libraries in a Blazor application.

Goodbye for now πŸ‘‹πŸ»

Top comments (0)