Introduction

Blazor allows developers to build modern web applications using C# instead of JavaScript. However, there are still many scenarios where you need to interact with JavaScript, such as using browser APIs, third-party libraries, charts, maps, or DOM manipulation.

This is where JavaScript Interop (JS Interop) comes into play.

JS Interop in Blazor enables communication between C# and JavaScript. You can call JavaScript functions from C#, and you can also call C# methods from JavaScript.

In this article, we will understand JS Interop in simple words, learn how to use it step by step, and most importantly, explore best practices to avoid performance issues in real-world Blazor applications.

What is JavaScript Interop in Blazor?

JavaScript Interop is a feature in Blazor that allows seamless communication between C# code and JavaScript code.

There are two main directions:

This helps when:

Calling JavaScript from Blazor (C# to JS)

To call JavaScript from Blazor, you use the IJSRuntime interface.

Step 1: Add JavaScript Function

Create a JavaScript file (for example: wwwroot/js/site.js):

JavaScript
function showAlert(message) {
    alert(message);
}

Include it in your HTML:

HTML / XML
<script src="js/site.js"></script>

Step 2: Inject IJSRuntime in Blazor

C#
@inject IJSRuntime JS

Step 3: Call JavaScript Function

C#
await JS.InvokeVoidAsync("showAlert", "Hello from Blazor!");

Explanation in simple words:

Calling JavaScript with Return Value

If your JavaScript function returns a value:

JavaScript
function getBrowserWidth() {
    return window.innerWidth;
}

Call from C#:

C#
int width = await JS.InvokeAsync<int>("getBrowserWidth");

This allows you to use JavaScript results inside your Blazor logic.

Calling C# from JavaScript (JS to .NET)

Blazor also allows JavaScript to call C# methods.

Step 1: Create C# Method

C#
[JSInvokable]
public static string SayHello()
{
    return "Hello from .NET";
}

Step 2: Call from JavaScript

JavaScript
DotNet.invokeMethodAsync('YourAssemblyName', 'SayHello')
    .then(result => console.log(result));

This is useful when integrating JavaScript-heavy libraries.

Using JS Modules (Recommended Approach)

Instead of global JS functions, use JavaScript modules for better performance and maintainability.

Step 1: Create Module File

JavaScript
export function showToast(message) {
    console.log(message);
}

Step 2: Import in Blazor

C#
IJSObjectReference module = await JS.InvokeAsync<IJSObjectReference>("import", "./js/site.js");
await module.InvokeVoidAsync("showToast", "Hello Module");

Why this is better:

Avoiding Performance Issues in JS Interop

JS Interop is powerful, but misuse can cause performance problems. Let’s understand how to avoid them.

1. Minimize Frequent Calls

Each JS interop call has overhead.

Bad practice:

C#
for (int i = 0; i < 1000; i++)
{
    await JS.InvokeVoidAsync("log", i);
}

Better approach:

C#
await JS.InvokeVoidAsync("logBatch", dataList);

2. Use Async Calls Properly

Always use async methods like InvokeAsync.

Why:

3. Prefer JS Modules Over Global Functions

Modules are loaded once and reused.

Benefit:

4. Avoid Large Data Transfers

Passing large objects between C# and JS is expensive.

Tip:

5. Cache JS References

Instead of importing modules repeatedly, cache them.

Example:

C#
private IJSObjectReference module;

protected override async Task OnInitializedAsync()
{
    module = await JS.InvokeAsync<IJSObjectReference>("import", "./js/site.js");
}

6. Dispose JS Resources Properly

Always dispose modules when not needed.

C#
await module.DisposeAsync();

This prevents memory leaks.

7. Use IJSInProcessRuntime (Blazor WebAssembly Only)

For synchronous calls in WebAssembly:

C#
var jsInProcess = (IJSInProcessRuntime)JS;
jsInProcess.InvokeVoid("fastCall");

Note:

Real-World Use Cases

JS Interop is commonly used for:

Example:

C#
await JS.InvokeVoidAsync("localStorage.setItem", "key", "value");

Common Mistakes to Avoid

Summary

JavaScript Interop in Blazor is essential for building real-world web applications that need browser APIs and external libraries. By using IJSRuntime, JS modules, and proper async patterns, developers can efficiently call JavaScript from C# and vice versa. To avoid performance issues, it is important to minimize calls, batch data, cache modules, and manage resources properly. With the right approach, JS Interop becomes a powerful tool for building scalable, high-performance Blazor applications in modern .NET development.