Pros and Cons of Using JavaScript Interop in Blazor
Detailed Blog page Skeleton loader
Pros and Cons of Using JavaScript Interop in Blazor

TLDR: Does JavaScript interop improve Blazor or complicate it? This blog evaluates the pros and cons. It discusses interop’s benefits like accessing browser APIs and existing JavaScript code integration. However, it also warns of performance issues, security risks, and added complexity. Developers integrating JavaScript into Blazor should weigh these factors.

Blazor is an open-source, single-page web application development framework. Unlike other frameworks, such as Angular, React and Vue, which depend on JavaScript libraries, Blazor allows you to write and run C# code in web browsers via WebAssembly.

The Blazor framework can, however, call JavaScript functions from .NET (C#) methods and vice versa. It handles the DOM (document object model) manipulation and browser API calls through a method called JavaScript interoperability (JS interop).

We can also use TypeScript in Blazor, as TypeScript is the superset of JavaScript. When we compile a Blazor project, the TypeScript file will be converted into a JavaScript file using the MSBuild properties.

In this blog post, we will see how to use JavaScript interop in Blazor, its pros, and its cons.

Invoke JavaScript functions from .NET methods

In a Blazor app, to call JavaScript functions from .NET, we need to inject the IJSRuntime abstraction and call the InvokeAsync method. The InvokeAsync method accepts the function name and number of arguments that the function requires.

ValueTask<TValue> InvokeAsync<TValue>(string identifier, object[] args);

This method has three arguments: function name, CancellationToken (for notification of whether the operation is canceled or not), and the number of arguments that the function requires.

ValueTask<TValue> InvokeAsync<TValue>(string identifier, CancellationToken cancellationToken, object[] args)

Explore the best and most comprehensive Blazor UI components library in the market.

Invoke .NET methods from JavaScript functions

To call static .NET methods from JavaScript functions in a Blazor app, use the DotNet.invokeMethod or DotNet.invokeMethodAsync method.

Refer to the following code example.

DotNet.invokeMethodAsync('{ASSEMBLY NAME}', '{.NET METHOD ID}', {ARGUMENTS});

The previous method has three arguments:

  • Application assembly name.
  • .NET method name (identifier).
  • The number of arguments (optional) that the function requires. (Note: Each argument should be JSON-serializable).

Pros of JS interop

Let’s see the advantages of using JavaScript interop in your Blazor application.

Injecting a script

Using the JavaScript interop, we can easily inject the required code on-demand anywhere in our Blazor app. You can see the injected JavaScript function after the DOM is loaded in the Blazor app (both WebAssembly and Server apps).

Follow these steps to inject a script file in Blazor:

  1. First, set the autostart attribute as false in the script tag <script>.
  2. Then, inject the required script using the start().then(…) method. The injected function will be called after starting the Blazor app.
  3. Next, create the script tag using the JavaScript function createElement and set the custom script file path in the src attribute.
  4. Finally, append the script element to the head method.

Refer to the following code example.

<body>
 <script src="_framework/blazor.{webassembly|server}.js"
         autostart="false"></script>
 <script>
   Blazor.start().then(function () {
      var customScript = document.createElement('script');
      customScript.setAttribute('src', 'scripts.js');
      document.head.appendChild(customScript);
   });
 </script>
</body>

JavaScript isolation

Blazor allows JavaScript isolation in standard JavaScript modules. This JavaScript isolation feature provides the following benefits:

  • JavaScript code will load only the specified components. This will save memory and rendering time.
  • Imported JavaScript code does not affect any global namespace.
  • We don’t need the library and component consumers to import the related JavaScript.

Everything a developer needs to know to use Blazor components in the Blazor app is completely documented.

Use any third-party JavaScript framework

One of the major advantages of using JavaScript interop is that we can integrate any of the JavaScript frameworks into the Blazor app and achieve its functionalities.

In the following code, we are going to call the Syncfusion JS 2 JavaScript library animation functions in our Blazor server app.

[_Host.cshtml]

<head>
  <link href="JSinterop.styles.css" rel="stylesheet" />
  @*Syncfusion library CDN script and CSS reference*@
  <link href="https://cdn.syncfusion.com/ej2/ej2-base/styles/material.css" rel="stylesheet">
  <script src="https://cdn.syncfusion.com/ej2/dist/ej2.min.js" type="text/javascript"></script>
  <script>
      window.initializeAnimation = () => {
         //initialize the Animation code.
         var animation = new ej.base.Animation({ duration: 5000 });
         animation.animate('#element1', { name: 'FadeOut' });
         animation.animate('#element2', { name: 'ZoomOut' });
      }
   </script>
</head>

[Index.razor]

@page "/"
@inject IJSRuntime JsRuntime;
<style>
  #element1, #element2 {
    background: #333333;
    border: 1px solid #cecece;
    box-sizing: border-box;
    float: left;
    height: 100px;
    width: 100px;
  }
</style>
<h1>Hello, world!</h1>
 
  <div id="element1"></div>
  <div id="element2"></div>
 
@code{
  bool firstRender = true;
  protected override async Task OnAfterRenderAsync(bool firstRender)
  {
    if (firstRender)
    {
      // call the JS function.
      await JsRuntime.InvokeVoidAsync(
            "initializeAnimation");
    }
  }
}

Cached JavaScript files

In a web platform, while caching static resources (the JavaScript file on the client-side browser), a static file will be loaded from the cache by default. This cache file will reduce the number of requests to the server, so it enhances the loading speed of the pages.

You can further improve the performance of the browser cache using the catche-control with the value of no-catch or by setting the max-age value.

Asynchronous JavaScript method calls

By default, the JavaScript interop calls asynchronous methods, so it is compatible with both Blazor server and WebAssembly apps.

Note: Blazor server’s JS interop calls should be asynchronous, as they’re sent over a network connection.

See how Syncfusion Blazor components can be transformed into beautiful and efficient applications.

Cons of JavaScript interop

Even though the JavaScript interop in Blazor has so many advantages, there are some limitations to it.

Directly modifying the DOM with JavaScript isn’t recommended

The DOM is used for display purposes alone in browsers. Modifying the DOM using JavaScript code is not suggested, anyway, as JavaScript restricts updating a Blazor element’s tracked changes.

Consider a situation where an element is rendered by Blazor code and modified externally using JavaScript directly or via JavaScript interop. Then, the DOM structure may not match Blazor’s internal representation. This may lead to undefined behaviors.

JavaScript interop call timeouts

The JavaScript interop call may fail due to a network issue and low bandwidth network in a server app. By default, it takes a one-minute timeout for each JavaScript call. Luckily, we have an option to increase the timeout value in the server app. But this may also lead to performance deterioration.

Refer to the following code.

[Program.CS]

builder.Services.AddServerSideBlazor(
  options => options.JSInteropDefaultCallTimeout = {TIMEOUT});

We can override the global timeout set using the JSInteropDefaultCallTimeout method.

[C#]

var result = await JS.InvokeAsync<string>("{ID}", {TIMEOUT}, new[] { "Arg1" });

Size limits on JavaScript interop calls

In a Blazor server app, JavaScript interop calls are limited in size while maximizing the incoming SignalR message permitted for hub methods.

If we pass a huge message (more than 32 KB) in a JS call, then it will throw an error.

However, we can increase the size of the SignalR message in the Program.CS file like in the following code.

[Program.CS]

builder.Services.AddServerSideBlazor()
  .AddHubOptions(options => options.MaximumReceiveMessageSize = 64 * 1024);

Increasing the message size will also increase the risks for the user. Additionally, reading a huge amount of content into memory as strings or byte arrays can also result in poor allocation of memory with the garbage collector. Further, it results in additional performance penalties.

Other performance issues

A Blazor app will experience poor performance when we serialize a huge amount of .NET objects and sent them to the JS interop call. For example, serializing huge .NET objects rapidly in the resize and mouse wheel events.

Calling the JS interop frequently may lead to performance lag. The synchronous calls that don’t perform JSON serialization of arguments or return values, the memory management, and translations between .NET and JavaScript also result in poor performance.

Syncfusion Blazor components can be transformed into stunning and efficient web apps.

Conclusion

Thanks for reading! In this blog, we have seen JavaScript interop concepts used in the Blazor framework along with their pros and cons. With this JS interop, we can easily call JavaScript functions from .NET (C#) methods and vice versa.

Syncfusion’s Blazor component suite offers over 70 UI components that work with both server-side and client-side (WebAssembly) hosting models seamlessly. Use them to build marvelous applications!

If you have questions, you can contact us through our support forumsupport portal, or feedback portal. As always, we are happy to assist you!

Related blogs

Be the first to get updates

Saravanan G

Meet the Author

Saravanan G

Saravanan is a Technical Product Manager at Syncfusion for Web products. He is passionate about Web technology and has been active in development since 2010 and focuses mainly on delivering the products with perfection.

Comments (1)

In regards to your comment about serializing large objects and seeing delays with JSRuntime. I noted that if I used the technique of opening a window and passing in it’s image or pdf content rle encoded, the memory was never returned. I think it was something to do with the circuit closing — and a ref staying somewhere keeping the GC from recycling. I reported it to .net team, but just got a genius informing me that, of course i was losing memory because I passed such a lot of memory to it — which I was to demo the leak. lol. I’m sure they’ll fix the issue at some point when someone not quite so lazy notes the issue.

Comments are closed.