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:
You need browser APIs like localStorage, geolocation, or clipboard
You want to use existing JavaScript libraries
You need fine control over DOM elements
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):
function showAlert(message) {
alert(message);
}
Include it in your HTML:
<script src="js/site.js"></script>
Step 2: Inject IJSRuntime in Blazor
@inject IJSRuntime JS
Step 3: Call JavaScript Function
await JS.InvokeVoidAsync("showAlert", "Hello from Blazor!");
Explanation in simple words:
Calling JavaScript with Return Value
If your JavaScript function returns a value:
function getBrowserWidth() {
return window.innerWidth;
}
Call from 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
[JSInvokable]
public static string SayHello()
{
return "Hello from .NET";
}
Step 2: Call from 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
export function showToast(message) {
console.log(message);
}
Step 2: Import in Blazor
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:
for (int i = 0; i < 1000; i++)
{
await JS.InvokeVoidAsync("log", i);
}
Better approach:
await JS.InvokeVoidAsync("logBatch", dataList);
2. Use Async Calls Properly
Always use async methods like InvokeAsync.
Why:
Prevents UI blocking
Improves responsiveness
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:
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.
await module.DisposeAsync();
This prevents memory leaks.
7. Use IJSInProcessRuntime (Blazor WebAssembly Only)
For synchronous calls in WebAssembly:
var jsInProcess = (IJSInProcessRuntime)JS;
jsInProcess.InvokeVoid("fastCall");
Note:
Real-World Use Cases
JS Interop is commonly used for:
Charts (Chart.js, D3.js)
Maps (Google Maps, Leaflet)
Browser storage (localStorage, sessionStorage)
File downloads and uploads
DOM manipulation
Example:
await JS.InvokeVoidAsync("localStorage.setItem", "key", "value");
Common Mistakes to Avoid
Calling JS too frequently
Not handling async properly
Forgetting to dispose modules
Sending large data unnecessarily
Using global JS functions everywhere
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.