--- title: Call JavaScript functions from .NET methods in ASP.NET Core Blazor author: guardrex description: Learn how to invoke JavaScript functions from .NET methods in Blazor apps. monikerRange: '>= aspnetcore-3.1' ms.author: riande ms.custom: mvc ms.date: 07/07/2020 no-loc: [cookie, Cookie, Blazor, "Blazor Server", "Blazor WebAssembly", "Identity", "Let's Encrypt", Razor, SignalR] uid: blazor/call-javascript-from-dotnet --- # Call JavaScript functions from .NET methods in ASP.NET Core Blazor By [Javier Calvarro Nelson](https://github.com/javiercn), [Daniel Roth](https://github.com/danroth27), and [Luke Latham](https://github.com/guardrex) A Blazor app can invoke JavaScript functions from .NET methods and .NET methods from JavaScript functions. These scenarios are called *JavaScript interoperability* (*JS interop*). This article covers invoking JavaScript functions from .NET. For information on how to call .NET methods from JavaScript, see . [View or download sample code](https://github.com/dotnet/AspNetCore.Docs/tree/master/aspnetcore/blazor/common/samples/) ([how to download](xref:index#how-to-download-a-sample)) To call into JavaScript from .NET, use the abstraction. To issue JS interop calls, inject the abstraction in your component. takes an identifier for the JavaScript function that you wish to invoke along with any number of JSON-serializable arguments. The function identifier is relative to the global scope (`window`). If you wish to call `window.someScope.someFunction`, the identifier is `someScope.someFunction`. There's no need to register the function before it's called. The return type `T` must also be JSON serializable. `T` should match the .NET type that best maps to the JSON type returned. For Blazor Server apps with prerendering enabled, calling into JavaScript isn't possible during the initial prerendering. JavaScript interop calls must be deferred until after the connection with the browser is established. For more information, see the [Detect when a Blazor Server app is prerendering](#detect-when-a-blazor-server-app-is-prerendering) section. The following example is based on [`TextDecoder`](https://developer.mozilla.org/docs/Web/API/TextDecoder), a JavaScript-based decoder. The example demonstrates how to invoke a JavaScript function from a C# method that offloads a requirement from developer code to an existing JavaScript API. The JavaScript function accepts a byte array from a C# method, decodes the array, and returns the text to the component for display. Inside the `` element of `wwwroot/index.html` (Blazor WebAssembly) or `Pages/_Host.cshtml` (Blazor Server), provide a JavaScript function that uses `TextDecoder` to decode a passed array and return the decoded value: [!code-html[](call-javascript-from-dotnet/samples_snapshot/index-script-convertarray.html)] JavaScript code, such as the code shown in the preceding example, can also be loaded from a JavaScript file (`.js`) with a reference to the script file: ```html ``` The following component: * Invokes the `convertArray` JavaScript function using `JSRuntime` when a component button (**`Convert Array`**) is selected. * After the JavaScript function is called, the passed array is converted into a string. The string is returned to the component for display. [!code-razor[](call-javascript-from-dotnet/samples_snapshot/call-js-example.razor?highlight=2,34-35)] ## IJSRuntime To use the abstraction, adopt any of the following approaches: * Inject the abstraction into the Razor component (`.razor`): [!code-razor[](call-javascript-from-dotnet/samples_snapshot/inject-abstraction.razor?highlight=1)] Inside the `` element of `wwwroot/index.html` (Blazor WebAssembly) or `Pages/_Host.cshtml` (Blazor Server), provide a `handleTickerChanged` JavaScript function. The function is called with and doesn't return a value: [!code-html[](call-javascript-from-dotnet/samples_snapshot/index-script-handleTickerChanged1.html)] * Inject the abstraction into a class (`.cs`): [!code-csharp[](call-javascript-from-dotnet/samples_snapshot/inject-abstraction-class.cs?highlight=5)] Inside the `` element of `wwwroot/index.html` (Blazor WebAssembly) or `Pages/_Host.cshtml` (Blazor Server), provide a `handleTickerChanged` JavaScript function. The function is called with `JSRuntime.InvokeAsync` and returns a value: [!code-html[](call-javascript-from-dotnet/samples_snapshot/index-script-handleTickerChanged2.html)] * For dynamic content generation with [BuildRenderTree](xref:blazor/advanced-scenarios#manual-rendertreebuilder-logic), use the `[Inject]` attribute: ```razor [Inject] IJSRuntime JSRuntime { get; set; } ``` In the client-side sample app that accompanies this topic, two JavaScript functions are available to the app that interact with the DOM to receive user input and display a welcome message: * `showPrompt`: Produces a prompt to accept user input (the user's name) and returns the name to the caller. * `displayWelcome`: Assigns a welcome message from the caller to a DOM object with an `id` of `welcome`. `wwwroot/exampleJsInterop.js`: [!code-javascript[](./common/samples/3.x/BlazorWebAssemblySample/wwwroot/exampleJsInterop.js?highlight=2-7)] Place the ` ``` `Pages/Index.razor` (parent component): ```razor @page "/"

Hello, world!

Welcome to your new app. ``` `Pages/Index.razor.cs`: ```csharp using System; using System.Collections.Generic; using Microsoft.AspNetCore.Components; namespace {APP ASSEMBLY}.Pages { public partial class Index : ComponentBase, IObservable, IDisposable { private bool disposing; private IList> subscriptions = new List>(); private ElementReference title; protected override void OnAfterRender(bool firstRender) { base.OnAfterRender(firstRender); foreach (var subscription in subscriptions) { try { subscription.OnNext(title); } catch (Exception) { throw; } } } public void Dispose() { disposing = true; foreach (var subscription in subscriptions) { try { subscription.OnCompleted(); } catch (Exception) { } } subscriptions.Clear(); } public IDisposable Subscribe(IObserver observer) { if (disposing) { throw new InvalidOperationException("Parent being disposed"); } subscriptions.Add(observer); return new Subscription(observer, this); } private class Subscription : IDisposable { public Subscription(IObserver observer, Index self) { Observer = observer; Self = self; } public IObserver Observer { get; } public Index Self { get; } public void Dispose() { Self.subscriptions.Remove(Observer); } } } } ``` The placeholder `{APP ASSEMBLY}` is the app's app assembly name (for example, `BlazorSample`). `Shared/SurveyPrompt.razor` (child component): ```razor @inject IJSRuntime JS @code { [Parameter] public string Title { get; set; } } ``` `Shared/SurveyPrompt.razor.cs`: ```csharp using System; using Microsoft.AspNetCore.Components; namespace {APP ASSEMBLY}.Shared { public partial class SurveyPrompt : ComponentBase, IObserver, IDisposable { private IDisposable subscription = null; [Parameter] public IObservable Parent { get; set; } protected override void OnParametersSet() { base.OnParametersSet(); if (subscription != null) { subscription.Dispose(); } subscription = Parent.Subscribe(this); } public void OnCompleted() { subscription = null; } public void OnError(Exception error) { subscription = null; } public void OnNext(ElementReference value) { JS.InvokeAsync( "setElementClass", new object[] { value, "red" }); } public void Dispose() { subscription?.Dispose(); } } } ``` The placeholder `{APP ASSEMBLY}` is the app's app assembly name (for example, `BlazorSample`). ## Harden JS interop calls JS interop may fail due to networking errors and should be treated as unreliable. By default, a Blazor Server app times out JS interop calls on the server after one minute. If an app can tolerate a more aggressive timeout, set the timeout using one of the following approaches: * Globally in `Startup.ConfigureServices`, specify the timeout: ```csharp services.AddServerSideBlazor( options => options.JSInteropDefaultCallTimeout = TimeSpan.FromSeconds({SECONDS})); ``` * Per-invocation in component code, a single call can specify the timeout: ```csharp var result = await JSRuntime.InvokeAsync("MyJSOperation", TimeSpan.FromSeconds({SECONDS}), new[] { "Arg1" }); ``` For more information on resource exhaustion, see . [!INCLUDE[](~/includes/blazor-share-interop-code.md)] ## Avoid circular object references Objects that contain circular references can't be serialized on the client for either: * .NET method calls. * JavaScript method calls from C# when the return type has circular references. For more information, see the following issues: * [Circular references are not supported, take two (dotnet/aspnetcore #20525)](https://github.com/dotnet/aspnetcore/issues/20525) * [Proposal: Add mechanism to handle circular references when serializing (dotnet/runtime #30820)](https://github.com/dotnet/runtime/issues/30820) ## Additional resources * * [InteropComponent.razor example (dotnet/AspNetCore GitHub repository, 3.1 release branch)](https://github.com/dotnet/AspNetCore/blob/release/3.1/src/Components/test/testassets/BasicTestApp/InteropComponent.razor) * [Perform large data transfers in Blazor Server apps](xref:blazor/advanced-scenarios#perform-large-data-transfers-in-blazor-server-apps)