Blazor channel icon Blazor · 35 published articles

Blazor WASM Javascript Interop and Isolation

In this post I will teach you how to interact with javascript and isolate javascript in your Blazor applications. All with live working demo.

Blazor WASM Javascript Interop and Isolation
On this page

What you'll learn

In this article let's learn about Javascript Interop and Javascript Isolation.

Note: If you have not done so already, I recommend you read the article on Blazor WASM Styles and CSS Isolation.

Why it matters

In Blazor WASM we should try to have minimal usage of Javascript. Though Blazor WASM runs on top of web assembly, we might sometime require access to javascript for certain operations like displaying alert, prompt setting theme on html element, focusing a suggestion from autocomplete input on up and down keyboard key press, getting viewport size or at few cases we might need to use the existing javascript library to render chart, grid, etc.

The above usecases can be solved in two different ways as mentioned below.

Implementation

Javascript Interop

To start using Javascript using JS interop, first we need our javascript code in a separate js file place inside wwwroot and we need to inject IJSRuntime in the component to invoke our javascript function.

Let's look on how to display a simple javascript alert in blazor. We'll start with defining a blazorInterop object just to make sure we don't pollute the global window object. Now let's register a simple displayAlert function which will take text and display it in javascript alert().

JavaScript


var blazorInterop = blazorInterop || {};

blazorInterop.displayAlert = (textToDisplay) => { alert(textToDisplay); };
        
Calling JS from .Net

Before we proceed on invoking the function make sure to add a reference to your javascript file in index.html. Now let's inject IJSRuntime into our component and call blazorInterop.displayAlert("I ❤️ DotNet") using InvokeVoidAsync as we don't have any return value from the javascript function.

Razor


<button @onclick="() => DisplayAlert()">Display Alert</button>

@code {
    [Inject] private IJSRuntime JSRuntime { get; set; } = default!;

    private async Task DisplayAlert()
    {
        await JSRuntime.InvokeVoidAsync("blazorInterop.displayAlert", "I ❤️ DotNet");
    }
}
        
Live demo: Javascript Interop - Displaying Alert — interactive controls are available when the page loads.

Voila.. now we are using javascript logics in our blazor app. That's a fairly simple example. Let's look at another example where we need some values from javascript function. If we want to find the current viewport width and height, then we can leverage javascript to get window height and width and return it back to blazor. The steps are same like before except this time we need to call the javascript function using InvokeAsync<WindowDimensions>("blazorInterop.getViewportDimensions").

JavaScript


var blazorInterop = blazorInterop || {};

blazorInterop.getViewportDimensions = () => {
    return { width: window.innerWidth, height: window.innerHeight };
}
        
Razor


<button @onclick="() => DisplayAlert()">Get Dimensions</button>

<p>Window Width: @windowDimensions.Width px</p>
<p>Window Height: @windowDimensions.Height px</p>

@code {
    private WindowDimensions windowDimensions = new();

    [Inject] private IJSRuntime JSRuntime { get; set; } = default!;

    private async Task DisplayAlert()
    {
        windowDimensions = await JSRuntime.InvokeAsync<WindowDimensions>("blazorInterop.getViewportDimensions");
        Console.WriteLine(windowDimensions.Height);
    }

    public class WindowDimensions
    {
        public int Width { get; set; }        
        public int Height { get; set; }
    }
}
        
Live demo: Javascript Interop - Finding Viewport Dimensions — interactive controls are available when the page loads.
Calling .Net from JS

There are situations where you need to call a .Net method from JS function. One example is clicking on a point in chart to display additional information. Now you need to pass some information from JS to .NET and make API call to retrieve additional information. You can call static method or .NET instance method from JS. Let's see how to do this in blazor.

Invoking Static Method

Calling a static method is pretty simple. All you need to do is to add [JSInvokable] attribute to your .NET static method and call the static .Net method from JS using DotNet.invokeMethod('Namespace', '.Net Static Method Name').

JavaScript


var blazorInterop = blazorInterop || {};

blazorInterop.callDotNetStaticFromJs = () => {
    let message = DotNet.invokeMethod('Web', 'GetMessageFromDotNet');
    alert(`Message from .Net Static Method - ${message}`);
}
        
Razor


<button @onclick="() => DisplayAlert()">Calling .Net Static Method from JS</button>

@code {
    [Inject] private IJSRuntime JSRuntime { get; set; } = default!;

    private async Task DisplayAlert()
    {
        await JSRuntime.InvokeVoidAsync("blazorInterop.callDotNetStaticFromJs");
    }

    [JSInvokable]
    public static string GetMessageFromDotNet()
    {
        return "DotNet ❤️ JS";
    }
}
        
Live demo: Javascript Interop - Calling .Net Static Method From JS — interactive controls are available when the page loads.
Invoking Instance Method

Calling an component instance method needs an .NET instance by reference to be passed to JS. This can be done by wrapping the component instance inside DotNetObjectReference<ComponentInstance> and calling create on it. Then we can invoke .NET method from JS using InvokeMethod or InvokeMethodAsync from the passed instance reference. Finally we need to dispose the DotNetObjectReference.

JavaScript


var blazorInterop = blazorInterop || {};

blazorInterop.callDotNetInstanceFromJs = (dotNetHelper) => {
    let message = dotNetHelper.invokeMethod('GetMessageFromDotNet');
    alert(`Message from .Net Static Method - ${message}`);
}
        
Razor

@implements IDisposable

<button @onclick="() => DisplayAlert()">Calling .Net instance Method from JS</button>

@code {
    private DotNetObjectReference<DotNetInstanceMethodFromJavascript>? objRef;

    [Inject] private IJSRuntime JSRuntime { get; set; } = default!;

    private async Task DisplayAlert()
    {
        objRef = DotNetObjectReference.Create(this);
        await JSRuntime.InvokeVoidAsync("blazorInterop.callDotNetInstanceFromJs", objRef);
    }

    [JSInvokable]
    public string GetMessageFromDotNet()
    {
        return "DotNet ❤️ JS";
    }

    public void Dispose()
    {
        objRef?.Dispose();
    }
}
        
Live demo: Javascript Interop - Calling .Net Instance Method From JS — interactive controls are available when the page loads.

Javascript Isolation

Blazor supports JS isolation in standard JS modules. This has the following benefits:

  1. No need to add reference to js file in index.html
  2. Imported JS no longer pollutes the global namespace.
  3. Consumers of a library and components aren't required to import the related JS.

With Javascript Isolation, the steps are same but with an extra step to load JS Module using IJSRuntime into IJSObjectReference (module). After loading the module, we need to call JS functions using IJSObjectReference module InvokeAsync instead of IJSRuntime. We also need to make sure the module is disposed on component dispose.

JavaScript


export function showPrompt(message) {
    return prompt(message, 'Type anything here');
}
        
Razor

@implements IAsyncDisposable

<button @onclick="TriggerPrompt">Trigger browser window prompt</button>

<p>Your message: @result</p>

@code {
    private IJSObjectReference? module;
    private string? result;

    [Inject] private IJSRuntime JSRuntime { get; set; } = default!;

    protected override async Task OnAfterRenderAsync(bool firstRender)
    {
        if (firstRender)
        {
            module = await JSRuntime.InvokeAsync<IJSObjectReference>("import","./js/demo/jsisolation.js");
        }
    }

    private async Task TriggerPrompt()
    {
        result = await Prompt("Provide some text");
    }

    public async ValueTask<string?> Prompt(string message) 
    {
        if(module is not null) 
        {
            return await module.InvokeAsync<string>("showPrompt", message);
        }

        return null;
    }

    async ValueTask IAsyncDisposable.DisposeAsync()
    {
        if (module is not null)
        {
            await module.DisposeAsync();
        }
    }
}
        
Live demo: Javascript Isolation - Calling Javascript prompt from JS Module — interactive controls are available when the page loads.

Summary

I'm happy that you have reached to the end of this article. We learned what is Javascript Interop and Javascript Isolation. We saw how to call JS functions from .NET and vice versa with a live working demo. I hope you enjoyed this.