Calling .NET Instance Methods in ASP.NET Core Blazor Directly from JavaScript

I ran into an issue recently where I needed to call some C# code from JavaScript. The Microsoft documentation on JavaScript to .NET interop is very detailed and covers a lot of scenarios including the one that would seem to fit the bill (invoking instance methods), but my requirements meant that I couldn’t use this exact approach.

In the example code the JavaScript isn’t being called directly; it’s .NET calling JavaScript which in turn calls the .NET method you want. The reason for this somewhat circular approach is that we need to pass an instance of our current .NET class to the JavaScript so that it can act upon it and call non-static methods. As such, the steps in the example are:

  1. Click the button, calling a C# method (TriggerDotNetInstanceMethod)
  2. In TriggerDotNetInstanceMethod:
    1. Bundle up the class using DotNetObjectReference.Create(this)
    2. Send the bundled class instance through to the JavaScript function using InvokeAsync
  3. On the JavaScript side, receive the class instance as a parameter and use it to call the C# method (optionally passing parameters as well)

This works well for a situation where you’re in control of that initial call (such as with a button press) and it does allow you to call .NET methods from JavaScript, but if you’re trying to provide a callback to a 3rd party JS library or have another reason that you can’t call the JavaScript from .NET first the above approach won’t work as outlined since you can’t grab the class instance directly from JavaScript.

Thankfully there is a way to resolve this!


I’ve put together a demo solution that shows this approach in action! The code is based on the Microsoft documentation on JavaScript to .NET interop with some tweaks.

In order to call the JavaScript directly without having to kick it off from C# first, we’ll need to modify the Microsoft example by breaking it into two discrete steps; bundling/sending the class instance and invoking a method on it.

Bring on the code!

Setting the Class Instance

Step 1 is to pass the class instance to JavaScript since we’ll need access to it later. To do this we need to add calling code in the Blazor page and a JavaScript function to accept and store the instance.

Here we have the code-behind file Index.razor.cs:

using Microsoft.AspNetCore.Components;
using Microsoft.JSInterop;

namespace InvokeDotNETInstanceMethodFromJavaScript.Pages;

public partial class Index: IDisposable
    [Inject] private IJSRuntime? JsRuntime { get; set; }
    private DotNetObjectReference<Index>? _objRef;

    protected override async Task OnAfterRenderAsync(bool firstRender)
        await BundleAndSendDotNetHelper();
        await base.OnAfterRenderAsync(firstRender);

    private async Task BundleAndSendDotNetHelper()
        _objRef = DotNetObjectReference.Create(this);
        if (JsRuntime != null)
            await JsRuntime.InvokeAsync<string>("SetDotNetHelper", _objRef);

    public void Dispose()

When the page calls OnAfterRenderAsync as part of the render lifecycle it invokes BundleAndSendDotNetHelper. This method has two statements: the first creates _objRef by rolling up the current class instance, while the second calls a JavaScript function called SetDotNetHelper using InvokeAsync and passes in that class instance. SetDotNetHelper is stored in a file called BlazorInterop.js and referenced in _Layout.cshtml so we have access to it across the app:

function SetDotNetHelper(dotNetHelper) {
    window.dotNetHelper = dotNetHelper;

This takes the class instance we’ve passed from C# and stores it as a global variable for later retrieval and use!

Invoking the Method

So we’ve done the setup, how do we go about actually invoking it?

Let’s first add a button to Index.razor so we can fire off a JavaScript function:

<button onclick="CallBlazorMethod()">Call JavaScript Function</button>

Since CallBlazorMethod isn’t declared yet, we’ll need to add that to our new BlazorInterop.js file:

function CallBlazorMethod()

This grabs the global dotNetHelper instance we stored earlier (by referencing it with window.dotNetHelper) and invokes a method on it called BlazorMethod. We’ll need to add this to our Index.razor.cs:

private int _currentNumber;

public void BlazorMethod()

Here we can see the JSInvokable attribute which is the magic that allows JavaScript to invoke the method (hence the name)!

I’ve also added an integer so that we can increment and display it on Index.razor:

<p>The number is currently set to: @_currentNumber</p>

And that’s it!

A screenshot of a Blazor project demonstrating the ability to invoke .NET instance methods from JavaScript by using a counter and a button that says "Call JavaScript Function"


Let’s take a high level view of what we’ve got set up now:

  1. OnAfterRenderAsync in Index.razor.cs calls the C# method BundleAndSendDotNetHelper as part of the render lifecycle
  2. BundleAndSendDotNetHelper bundles the current class instance and sends it through to the JavaScript function SetDotNetHelper
  3. SetDotNetHelper takes the class instance and stores it globally so that we can access it later
  4. The JavaScript function CallBlazorMethod is invoked (from a button press, a callback etc), which grabs the global dotNetHelper instance and calls the .NET method BlazorMethod on it
  5. BlazorMethod increments the _currentNumber variable and calls StateHasChanged to trigger a re-render of the page


Calling .NET instance methods from JavaScript can take a bit of setup, but once configured it works pretty seamlessly! I still prefer to keep the page/business logic in the land of .NET as far as possible since it promotes code re-use and allows for more type safety, but for those situations where you can’t keep it all in one place this approach (or the other approaches listed in the Microsoft documentation for JavaScript to .NET interop) should fit the bill!

Leave a Reply

Fill in your details below or click an icon to log in: Logo

You are commenting using your account. Log Out /  Change )

Twitter picture

You are commenting using your Twitter account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s

This site uses Akismet to reduce spam. Learn how your comment data is processed.