--- title: Call .NET methods from JavaScript functions in ASP.NET Core Blazor author: guardrex description: Learn how to invoke .NET methods from JavaScript functions in Blazor apps. monikerRange: '>= aspnetcore-3.1' ms.author: riande ms.custom: mvc ms.date: 11/12/2024 uid: blazor/js-interop/call-dotnet-from-javascript --- # Call .NET methods from JavaScript functions in ASP.NET Core Blazor [!INCLUDE[](~/includes/not-latest-version.md)] This article explains how to invoke .NET methods from JavaScript (JS). For information on how to call JS functions from .NET, see . ## Invoke a static .NET method To invoke a static .NET method from JavaScript (JS), use the JS functions: * `DotNet.invokeMethodAsync` (*recommended*): Asynchronous for both server-side and client-side components. * `DotNet.invokeMethod`: Synchronous for client-side components only. Pass in the name of the assembly containing the method, the identifier of the static .NET method, and any arguments. In the following example: * The `{ASSEMBLY NAME}` placeholder is the app's assembly name. * The `{.NET METHOD ID}` placeholder is the .NET method identifier. * The `{ARGUMENTS}` placeholder are optional, comma-separated arguments to pass to the method, each of which must be JSON-serializable. ```javascript DotNet.invokeMethodAsync('{ASSEMBLY NAME}', '{.NET METHOD ID}', {ARGUMENTS}); ``` `DotNet.invokeMethodAsync` returns a [JS `Promise`](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Promise) representing the result of the operation. `DotNet.invokeMethod` (client-side components) returns the result of the operation. > [!IMPORTANT] > For server-side components, we recommend the asynchronous function (`invokeMethodAsync`) over the synchronous version (`invokeMethod`). The .NET method must be public, static, and have the [`[JSInvokable]` attribute](xref:Microsoft.JSInterop.JSInvokableAttribute). In the following example: * The `{}` placeholder indicates the return type, which is only required for methods that return a value. * The `{.NET METHOD ID}` placeholder is the method identifier. ```razor @code { [JSInvokable] public static Task{} {.NET METHOD ID}() { ... } } ``` > [!NOTE] > Calling open generic methods isn't supported with static .NET methods but is supported with instance methods. For more information, see the [Call .NET generic class methods](#call-net-generic-class-methods) section. In the following component, the `ReturnArrayAsync` C# method returns an `int` array. The [`[JSInvokable]` attribute](xref:Microsoft.JSInterop.JSInvokableAttribute) is applied to the method, which makes the method invokable by JS. :::moniker range=">= aspnetcore-9.0" `CallDotnet1.razor`: :::code language="razor" source="~/../blazor-samples/9.0/BlazorSample_BlazorWebApp/Components/Pages/CallDotnet1.razor"::: `CallDotnet1.razor.js`: :::code language="javascript" source="~/../blazor-samples/9.0/BlazorSample_BlazorWebApp/Components/Pages/CallDotnet1.razor.js"::: The `addHandlers` JS function adds a [`click`](https://developer.mozilla.org/docs/Web/API/Element/click_event) event to the button. The `returnArrayAsync` JS function is assigned as the handler. The `returnArrayAsync` JS function calls the `ReturnArrayAsync` .NET method of the component, which logs the result to the browser's web developer tools console. `BlazorSample` is the app's assembly name. :::moniker-end :::moniker range=">= aspnetcore-8.0 < aspnetcore-9.0" `CallDotnet1.razor`: :::code language="razor" source="~/../blazor-samples/8.0/BlazorSample_BlazorWebApp/Components/Pages/CallDotnet1.razor"::: `CallDotnet1.razor.js`: :::code language="javascript" source="~/../blazor-samples/8.0/BlazorSample_BlazorWebApp/Components/Pages/CallDotnet1.razor.js"::: The `addHandlers` JS function adds a [`click`](https://developer.mozilla.org/docs/Web/API/Element/click_event) event to the button. The `returnArrayAsync` JS function is assigned as the handler. The `returnArrayAsync` JS function calls the `ReturnArrayAsync` .NET method of the component, which logs the result to the browser's web developer tools console. `BlazorSample` is the app's assembly name. :::moniker-end :::moniker range=">= aspnetcore-7.0 < aspnetcore-8.0" `CallDotNetExample1.razor`: :::code language="razor" source="~/../blazor-samples/7.0/BlazorSample_WebAssembly/Pages/call-dotnet-from-js/CallDotNetExample1.razor"::: :::moniker-end :::moniker range=">= aspnetcore-6.0 < aspnetcore-7.0" `CallDotNetExample1.razor`: :::code language="razor" source="~/../blazor-samples/6.0/BlazorSample_WebAssembly/Pages/call-dotnet-from-js/CallDotNetExample1.razor"::: :::moniker-end :::moniker range=">= aspnetcore-5.0 < aspnetcore-6.0" `CallDotNetExample1.razor`: :::code language="razor" source="~/../blazor-samples/5.0/BlazorSample_WebAssembly/Pages/call-dotnet-from-js/CallDotNetExample1.razor"::: :::moniker-end :::moniker range="< aspnetcore-5.0" `CallDotNetExample1.razor`: :::code language="razor" source="~/../blazor-samples/3.1/BlazorSample_WebAssembly/Pages/call-dotnet-from-js/CallDotNetExample1.razor"::: :::moniker-end :::moniker range="< aspnetcore-8.0" The `

@code { private IJSObjectReference? module; private string? name; private DotNetObjectReference? dotNetHelper; protected override async Task OnAfterRenderAsync(bool firstRender) { if (firstRender) { module = await JS.InvokeAsync("import", "./Components/Pages/CallDotNetExampleOneHelper.razor.js"); dotNetHelper = DotNetObjectReference.Create(this); await module.InvokeVoidAsync("GreetingHelpers.setDotNetHelper", dotNetHelper); await module.InvokeVoidAsync("addHandlers"); } } [JSInvokable] public string GetHelloMessage() => $"Hello, {name}!"; [JSInvokable] public string GetWelcomeMessage() => $"Welcome, {name}!"; async ValueTask IAsyncDisposable.DisposeAsync() { if (module is not null) { try { await module.DisposeAsync(); } catch (JSDisconnectedException) { } } dotNetHelper?.Dispose(); } } ``` In the preceding example: * `JS` is an injected instance. is registered by the Blazor framework. * The variable name `dotNetHelper` is arbitrary and can be changed to any preferred name. * The component must explicitly dispose of the to permit garbage collection and prevent a memory leak. * is trapped during module disposal in case Blazor's SignalR circuit is lost. If the preceding code is used in a Blazor WebAssembly app, there's no SignalR connection to lose, so you can remove the `try`-`catch` block and leave the line that disposes the module (`await module.DisposeAsync();`). For more information, see . `CallDotNetExampleOneHelper.razor.js`: ```javascript export class GreetingHelpers { static dotNetHelper; static setDotNetHelper(value) { GreetingHelpers.dotNetHelper = value; } static async sayHello() { const msg = await GreetingHelpers.dotNetHelper.invokeMethodAsync('GetHelloMessage'); alert(`Message from .NET: "${msg}"`); } static async welcomeVisitor() { const msg = await GreetingHelpers.dotNetHelper.invokeMethodAsync('GetWelcomeMessage'); alert(`Message from .NET: "${msg}"`); } } export function addHandlers() { const sayHelloBtn = document.getElementById("sayHelloBtn"); sayHelloBtn.addEventListener("click", GreetingHelpers.sayHello); const welcomeVisitorBtn = document.getElementById("welcomeVisitorBtn"); welcomeVisitorBtn.addEventListener("click", GreetingHelpers.welcomeVisitor); } ``` In the preceding example, the variable name `dotNetHelper` is arbitrary and can be changed to any preferred name. :::moniker-end :::moniker range="< aspnetcore-8.0" ```csharp @page "/call-dotnet-example-one-helper" @implements IDisposable @inject IJSRuntime JS

Pass DotNetObjectReference to a JavaScript class

@code { private string? name; private DotNetObjectReference? dotNetHelper; protected override async Task OnAfterRenderAsync(bool firstRender) { if (firstRender) { dotNetHelper = DotNetObjectReference.Create(this); await JS.InvokeVoidAsync("GreetingHelpers.setDotNetHelper", dotNetHelper); } } [JSInvokable] public string GetHelloMessage() => $"Hello, {name}!"; [JSInvokable] public string GetWelcomeMessage() => $"Welcome, {name}!"; public void Dispose() { dotNetHelper?.Dispose(); } } ``` In the preceding example: * `JS` is an injected instance. is registered by the Blazor framework. * The variable name `dotNetHelper` is arbitrary and can be changed to any preferred name. * The component must explicitly dispose of the to permit garbage collection and prevent a memory leak. ```html ``` In the preceding example: * The `GreetingHelpers` class is added to the `window` object to globally define the class, which permits Blazor to locate the class for JS interop. * The variable name `dotNetHelper` is arbitrary and can be changed to any preferred name. :::moniker-end > [!NOTE] > For general guidance on JS location and our recommendations for production apps, see . :::moniker range=">= aspnetcore-6.0" ## Call .NET generic class methods JavaScript (JS) functions can call [.NET generic class](/dotnet/csharp/programming-guide/generics/generic-classes) methods, where a JS function calls a .NET method of a generic class. In the following generic type class (`GenericType`): * The class has a single type parameter (`TValue`) with a single generic `Value` property. * The class has two non-generic methods marked with the [`[JSInvokable]` attribute](xref:Microsoft.JSInterop.JSInvokableAttribute), each with a generic type parameter named `newValue`: * `Update` synchronously updates the value of `Value` from `newValue`. * `UpdateAsync` asynchronously updates the value of `Value` from `newValue` after creating an awaitable task with that asynchronously yields back to the current context when awaited. * Each of the class methods write the type of `TValue` and the value of `Value` to the console. Writing to the console is only for demonstration purposes. Production apps usually avoid writing to the console in favor of app *logging*. For more information, see and . > [!NOTE] > *Open generic types and methods* don't specify types for type placeholders. Conversely, *closed generics* supply types for all type placeholders. The examples in this section demonstrate closed generics, but invoking JS interop *instance methods* with open generics ***is supported***. Use of open generics isn't supported for [static .NET method invocations](#invoke-a-static-net-method), which were described earlier in this article. For more information, see the following articles: * [Generic classes and methods (C# documentation)](/dotnet/csharp/fundamentals/types/generics) * [Generic Classes (C# Programming Guide)](/dotnet/csharp/programming-guide/generics/generic-classes) * [Generics in .NET (.NET documentation)](/dotnet/standard/generics/) `GenericType.cs`: ```csharp using Microsoft.JSInterop; public class GenericType { public TValue? Value { get; set; } [JSInvokable] public void Update(TValue newValue) { Value = newValue; Console.WriteLine($"Update: GenericType<{typeof(TValue)}>: {Value}"); } [JSInvokable] public async void UpdateAsync(TValue newValue) { await Task.Yield(); Value = newValue; Console.WriteLine($"UpdateAsync: GenericType<{typeof(TValue)}>: {Value}"); } } ``` In the following `invokeMethodsAsync` function: * The generic type class's `Update` and `UpdateAsync` methods are called with arguments representing strings and numbers. * Client-side components support calling .NET methods synchronously with `invokeMethod`. `syncInterop` receives a boolean value indicating if the JS interop is occurring on the client. When `syncInterop` is `true`, `invokeMethod` is safely called. If the value of `syncInterop` is `false`, only the asynchronous function `invokeMethodAsync` is called because the JS interop is executing in a server-side component. * For demonstration purposes, the function call (`invokeMethod` or `invokeMethodAsync`), the .NET method called (`Update` or `UpdateAsync`), and the argument are written to the console. The arguments use a random number to permit matching the JS function call to the .NET method invocation (also written to the console on the .NET side). Production code usually doesn't write to the console, either on the client or the server. Production apps usually rely upon app *logging*. For more information, see and . ```html ``` > [!NOTE] > For general guidance on JS location and our recommendations for production apps, see . In the following `GenericsExample` component: * The JS function `invokeMethodsAsync` is called when the **`Invoke Interop`** button is selected. * A pair of types are created and passed to the JS function for instances of the `GenericType` as a `string` and an `int`. `GenericsExample.razor`: :::moniker-end :::moniker range=">= aspnetcore-8.0" ```razor @page "/generics-example" @implements IDisposable @inject IJSRuntime JS

  • genericType1: @genericType1?.Value
  • genericType2: @genericType2?.Value
@code { private GenericType genericType1 = new() { Value = "string 0" }; private GenericType genericType2 = new() { Value = 0 }; private DotNetObjectReference>? objRef1; private DotNetObjectReference>? objRef2; protected override void OnInitialized() { objRef1 = DotNetObjectReference.Create(genericType1); objRef2 = DotNetObjectReference.Create(genericType2); } public async Task InvokeInterop() { var syncInterop = OperatingSystem.IsBrowser(); await JS.InvokeVoidAsync( "invokeMethodsAsync", syncInterop, objRef1, objRef2); } public void Dispose() { objRef1?.Dispose(); objRef2?.Dispose(); } } ``` :::moniker-end :::moniker range=">= aspnetcore-6.0 < aspnetcore-8.0" ```razor @page "/generics-example" @implements IDisposable @inject IJSRuntime JS

  • genericType1: @genericType1?.Value
  • genericType2: @genericType2?.Value
@code { private GenericType genericType1 = new() { Value = "string 0" }; private GenericType genericType2 = new() { Value = 0 }; private DotNetObjectReference>? objRef1; private DotNetObjectReference>? objRef2; protected override void OnInitialized() { objRef1 = DotNetObjectReference.Create(genericType1); objRef2 = DotNetObjectReference.Create(genericType2); } public async Task InvokeInterop() { var syncInterop = OperatingSystem.IsBrowser(); await JS.InvokeVoidAsync( "invokeMethodsAsync", syncInterop, objRef1, objRef2); } public void Dispose() { objRef1?.Dispose(); objRef2?.Dispose(); } } ``` :::moniker-end :::moniker range=">= aspnetcore-6.0" In the preceding example, `JS` is an injected instance. is registered by the Blazor framework. The following demonstrates typical output of the preceding example when the **`Invoke Interop`** button is selected in a client-side component: > JS: invokeMethodAsync:Update('string 37802') > .NET: Update: GenericType: string 37802 > JS: invokeMethodAsync:UpdateAsync('string 53051') > JS: invokeMethod:Update('string 26784') > .NET: Update: GenericType: string 26784 > JS: invokeMethodAsync:Update(14107) > .NET: Update: GenericType: 14107 > JS: invokeMethodAsync:UpdateAsync(48995) > JS: invokeMethod:Update(12872) > .NET: Update: GenericType: 12872 > .NET: UpdateAsync: GenericType: string 53051 > .NET: UpdateAsync: GenericType: 48995 If the preceding example is implemented in a server-side component, the synchronous calls with `invokeMethod` are avoided. For server-side components, we recommend the asynchronous function (`invokeMethodAsync`) over the synchronous version (`invokeMethod`). Typical output of a server-side component: > JS: invokeMethodAsync:Update('string 34809') > .NET: Update: GenericType: string 34809 > JS: invokeMethodAsync:UpdateAsync('string 93059') > JS: invokeMethodAsync:Update(41997) > .NET: Update: GenericType: 41997 > JS: invokeMethodAsync:UpdateAsync(24652) > .NET: UpdateAsync: GenericType: string 93059 > .NET: UpdateAsync: GenericType: 24652 The preceding output examples demonstrate that asynchronous methods execute and complete in an *arbitrary order* depending on several factors, including thread scheduling and the speed of method execution. It isn't possible to reliably predict the order of completion for asynchronous method calls. :::moniker-end ## Class instance examples The following `sayHello1` JS function: * Calls the `GetHelloMessage` .NET method on the passed . * Returns the message from `GetHelloMessage` to the `sayHello1` caller. ```html ``` > [!NOTE] > For general guidance on JS location and our recommendations for production apps, see . In the preceding example, the variable name `dotNetHelper` is arbitrary and can be changed to any preferred name. The following `HelloHelper` class has a JS-invokable .NET method named `GetHelloMessage`. When `HelloHelper` is created, the name in the `Name` property is used to return a message from `GetHelloMessage`. `HelloHelper.cs`: :::moniker range=">= aspnetcore-9.0" :::code language="csharp" source="~/../blazor-samples/9.0/BlazorSample_BlazorWebApp/HelloHelper.cs"::: :::moniker-end :::moniker range=">= aspnetcore-8.0 < aspnetcore-9.0" :::code language="csharp" source="~/../blazor-samples/8.0/BlazorSample_BlazorWebApp/HelloHelper.cs"::: :::moniker-end :::moniker range=">= aspnetcore-7.0 < aspnetcore-8.0" :::code language="csharp" source="~/../blazor-samples/7.0/BlazorSample_WebAssembly/HelloHelper.cs"::: :::moniker-end :::moniker range=">= aspnetcore-6.0 < aspnetcore-7.0" :::code language="csharp" source="~/../blazor-samples/6.0/BlazorSample_WebAssembly/HelloHelper.cs"::: :::moniker-end :::moniker range=">= aspnetcore-5.0 < aspnetcore-6.0" :::code language="csharp" source="~/../blazor-samples/5.0/BlazorSample_WebAssembly/HelloHelper.cs"::: :::moniker-end :::moniker range="< aspnetcore-5.0" :::code language="csharp" source="~/../blazor-samples/3.1/BlazorSample_WebAssembly/HelloHelper.cs"::: :::moniker-end The `CallHelloHelperGetHelloMessage` method in the following `JsInteropClasses3` class invokes the JS function `sayHello1` with a new instance of `HelloHelper`. `JsInteropClasses3.cs`: :::moniker range=">= aspnetcore-9.0" :::code language="csharp" source="~/../blazor-samples/9.0/BlazorSample_BlazorWebApp/JsInteropClasses3.cs"::: :::moniker-end :::moniker range=">= aspnetcore-8.0 < aspnetcore-9.0" :::code language="csharp" source="~/../blazor-samples/8.0/BlazorSample_BlazorWebApp/JsInteropClasses3.cs"::: :::moniker-end :::moniker range=">= aspnetcore-7.0 < aspnetcore-8.0" :::code language="csharp" source="~/../blazor-samples/7.0/BlazorSample_WebAssembly/JsInteropClasses3.cs"::: :::moniker-end :::moniker range=">= aspnetcore-6.0 < aspnetcore-7.0" :::code language="csharp" source="~/../blazor-samples/6.0/BlazorSample_WebAssembly/JsInteropClasses3.cs"::: :::moniker-end :::moniker range=">= aspnetcore-5.0 < aspnetcore-6.0" :::code language="csharp" source="~/../blazor-samples/5.0/BlazorSample_WebAssembly/JsInteropClasses3.cs"::: :::moniker-end :::moniker range="< aspnetcore-5.0" :::code language="csharp" source="~/../blazor-samples/3.1/BlazorSample_WebAssembly/JsInteropClasses3.cs"::: :::moniker-end To avoid a memory leak and allow garbage collection, the .NET object reference created by is disposed when the object reference goes out of scope with [`using var` syntax](/dotnet/csharp/language-reference/keywords/using-statement). When the **`Trigger .NET instance method`** button is selected in the following component, `JsInteropClasses3.CallHelloHelperGetHelloMessage` is called with the value of `name`. :::moniker range=">= aspnetcore-9.0" `CallDotnet4.razor`: :::code language="razor" source="~/../blazor-samples/9.0/BlazorSample_BlazorWebApp/Components/Pages/CallDotnet4.razor"::: :::moniker-end :::moniker range=">= aspnetcore-8.0 < aspnetcore-9.0" `CallDotnet4.razor`: :::code language="razor" source="~/../blazor-samples/8.0/BlazorSample_BlazorWebApp/Components/Pages/CallDotnet4.razor"::: :::moniker-end :::moniker range=">= aspnetcore-7.0 < aspnetcore-8.0" `CallDotNetExample4.razor`: :::code language="razor" source="~/../blazor-samples/7.0/BlazorSample_WebAssembly/Pages/call-dotnet-from-js/CallDotNetExample4.razor"::: :::moniker-end :::moniker range=">= aspnetcore-6.0 < aspnetcore-7.0" `CallDotNetExample4.razor`: :::code language="razor" source="~/../blazor-samples/6.0/BlazorSample_WebAssembly/Pages/call-dotnet-from-js/CallDotNetExample4.razor"::: :::moniker-end :::moniker range=">= aspnetcore-5.0 < aspnetcore-6.0" `CallDotNetExample4.razor`: :::code language="razor" source="~/../blazor-samples/5.0/BlazorSample_WebAssembly/Pages/call-dotnet-from-js/CallDotNetExample4.razor"::: :::moniker-end :::moniker range="< aspnetcore-5.0" `CallDotNetExample4.razor`: :::code language="razor" source="~/../blazor-samples/3.1/BlazorSample_WebAssembly/Pages/call-dotnet-from-js/CallDotNetExample4.razor"::: :::moniker-end The following image shows the rendered component with the name `Amy Pond` in the `Name` field. After the button is selected, `Hello, Amy Pond!` is displayed in the UI: ![Rendered 'CallDotNetExample4' component example](~/blazor/javascript-interoperability/call-dotnet-from-javascript/_static/component-example-4.png) The preceding pattern shown in the `JsInteropClasses3` class can also be implemented entirely in a component. :::moniker range=">= aspnetcore-9.0" `CallDotnet5.razor`: :::code language="razor" source="~/../blazor-samples/9.0/BlazorSample_BlazorWebApp/Components/Pages/CallDotnet5.razor"::: :::moniker-end :::moniker range=">= aspnetcore-8.0 < aspnetcore-9.0" `CallDotnet5.razor`: :::code language="razor" source="~/../blazor-samples/8.0/BlazorSample_BlazorWebApp/Components/Pages/CallDotnet5.razor"::: :::moniker-end :::moniker range=">= aspnetcore-7.0 < aspnetcore-8.0" `CallDotNetExample5.razor`: :::code language="razor" source="~/../blazor-samples/7.0/BlazorSample_WebAssembly/Pages/call-dotnet-from-js/CallDotNetExample5.razor"::: :::moniker-end :::moniker range=">= aspnetcore-6.0 < aspnetcore-7.0" `CallDotNetExample5.razor`: :::code language="razor" source="~/../blazor-samples/6.0/BlazorSample_WebAssembly/Pages/call-dotnet-from-js/CallDotNetExample5.razor"::: :::moniker-end :::moniker range=">= aspnetcore-5.0 < aspnetcore-6.0" `CallDotNetExample5.razor`: :::code language="razor" source="~/../blazor-samples/5.0/BlazorSample_WebAssembly/Pages/call-dotnet-from-js/CallDotNetExample5.razor"::: :::moniker-end :::moniker range="< aspnetcore-5.0" `CallDotNetExample5.razor`: :::code language="razor" source="~/../blazor-samples/3.1/BlazorSample_WebAssembly/Pages/call-dotnet-from-js/CallDotNetExample5.razor"::: :::moniker-end To avoid a memory leak and allow garbage collection, the .NET object reference created by is disposed when the object reference goes out of scope with [`using var` syntax](/dotnet/csharp/language-reference/keywords/using-statement). The output displayed by the component is `Hello, Amy Pond!` when the name `Amy Pond` is provided in the `name` field. In the preceding component, the .NET object reference is disposed. If a class or component doesn't dispose the , dispose it from the client by calling `dispose` on the passed : ```javascript window.{JS FUNCTION NAME} = (dotNetHelper) => { dotNetHelper.invokeMethodAsync('{.NET METHOD ID}'); dotNetHelper.dispose(); } ``` In the preceding example: * The `{JS FUNCTION NAME}` placeholder is the JS function's name. * The variable name `dotNetHelper` is arbitrary and can be changed to any preferred name. * The `{.NET METHOD ID}` placeholder is the .NET method identifier. ## Component instance .NET method helper class A helper class can invoke a .NET instance method as an . Helper classes are useful in scenarios where using static .NET methods aren't applicable: * When several components of the same type are rendered on the same page. * In server-side apps with multiple users concurrently using the same component. In the following example: * The component contains several `ListItem1` components. * Each `ListItem1` component is composed of a message and a button. * When a `ListItem1` component button is selected, that `ListItem1`'s `UpdateMessage` method changes the list item text and hides the button. The following `MessageUpdateInvokeHelper` class maintains a JS-invokable .NET method, `UpdateMessageCaller`, to invoke the specified when the class is instantiated. `MessageUpdateInvokeHelper.cs`: :::moniker range=">= aspnetcore-9.0" :::code language="csharp" source="~/../blazor-samples/9.0/BlazorSample_BlazorWebApp/MessageUpdateInvokeHelper.cs"::: :::moniker-end :::moniker range=">= aspnetcore-8.0 < aspnetcore-9.0" :::code language="csharp" source="~/../blazor-samples/8.0/BlazorSample_BlazorWebApp/MessageUpdateInvokeHelper.cs"::: :::moniker-end :::moniker range=">= aspnetcore-7.0 < aspnetcore-8.0" :::code language="csharp" source="~/../blazor-samples/7.0/BlazorSample_WebAssembly/MessageUpdateInvokeHelper.cs"::: :::moniker-end :::moniker range=">= aspnetcore-6.0 < aspnetcore-7.0" :::code language="csharp" source="~/../blazor-samples/6.0/BlazorSample_WebAssembly/MessageUpdateInvokeHelper.cs"::: :::moniker-end :::moniker range=">= aspnetcore-5.0 < aspnetcore-6.0" :::code language="csharp" source="~/../blazor-samples/5.0/BlazorSample_WebAssembly/MessageUpdateInvokeHelper.cs"::: :::moniker-end :::moniker range="< aspnetcore-5.0" :::code language="csharp" source="~/../blazor-samples/3.1/BlazorSample_WebAssembly/MessageUpdateInvokeHelper.cs"::: :::moniker-end The following `updateMessageCaller` JS function invokes the `UpdateMessageCaller` .NET method. ```html ``` > [!NOTE] > For general guidance on JS location and our recommendations for production apps, see . In the preceding example, the variable name `dotNetHelper` is arbitrary and can be changed to any preferred name. The following `ListItem1` component is a shared component that can be used any number of times in a parent component and creates list items (`
  • ...
  • `) for an HTML list (`
      ...
    ` or `
      ...
    `). Each `ListItem1` component instance establishes an instance of `MessageUpdateInvokeHelper` with an set to its `UpdateMessage` method. When a `ListItem1` component's **`InteropCall`** button is selected, `updateMessageCaller` is invoked with a created for the `MessageUpdateInvokeHelper` instance. This permits the framework to call `UpdateMessageCaller` on that `ListItem1`'s `MessageUpdateInvokeHelper` instance. The passed is disposed in JS (`dotNetHelper.dispose()`). `ListItem1.razor`: :::moniker range=">= aspnetcore-9.0" :::code language="razor" source="~/../blazor-samples/9.0/BlazorSample_BlazorWebApp/Components/ListItem1.razor"::: :::moniker-end :::moniker range=">= aspnetcore-8.0 < aspnetcore-9.0" :::code language="razor" source="~/../blazor-samples/8.0/BlazorSample_BlazorWebApp/Components/ListItem1.razor"::: :::moniker-end :::moniker range=">= aspnetcore-7.0 < aspnetcore-8.0" :::code language="razor" source="~/../blazor-samples/7.0/BlazorSample_WebAssembly/Shared/call-dotnet-from-js/ListItem1.razor"::: :::moniker-end :::moniker range=">= aspnetcore-6.0 < aspnetcore-7.0" :::code language="razor" source="~/../blazor-samples/6.0/BlazorSample_WebAssembly/Shared/call-dotnet-from-js/ListItem1.razor"::: :::moniker-end :::moniker range=">= aspnetcore-5.0 < aspnetcore-6.0" :::code language="razor" source="~/../blazor-samples/5.0/BlazorSample_WebAssembly/Shared/call-dotnet-from-js/ListItem1.razor"::: :::moniker-end :::moniker range="< aspnetcore-5.0" :::code language="razor" source="~/../blazor-samples/3.1/BlazorSample_WebAssembly/Shared/call-dotnet-from-js/ListItem1.razor"::: :::moniker-end [`StateHasChanged`](xref:blazor/components/lifecycle#state-changes-statehaschanged) is called to update the UI when `message` is set in `UpdateMessage`. If `StateHasChanged` isn't called, Blazor has no way of knowing that the UI should be updated when the is invoked. The following parent component includes four list items, each an instance of the `ListItem1` component. :::moniker range=">= aspnetcore-9.0" `CallDotnet6.razor`: :::code language="razor" source="~/../blazor-samples/9.0/BlazorSample_BlazorWebApp/Components/Pages/CallDotnet6.razor"::: :::moniker-end :::moniker range=">= aspnetcore-8.0 < aspnetcore-9.0" `CallDotnet6.razor`: :::code language="razor" source="~/../blazor-samples/8.0/BlazorSample_BlazorWebApp/Components/Pages/CallDotnet6.razor"::: :::moniker-end :::moniker range=">= aspnetcore-7.0 < aspnetcore-8.0" `CallDotNetExample6.razor`: :::code language="razor" source="~/../blazor-samples/7.0/BlazorSample_WebAssembly/Pages/call-dotnet-from-js/CallDotNetExample6.razor"::: :::moniker-end :::moniker range=">= aspnetcore-6.0 < aspnetcore-7.0" `CallDotNetExample6.razor`: :::code language="razor" source="~/../blazor-samples/6.0/BlazorSample_WebAssembly/Pages/call-dotnet-from-js/CallDotNetExample6.razor"::: :::moniker-end :::moniker range=">= aspnetcore-5.0 < aspnetcore-6.0" `CallDotNetExample6.razor`: :::code language="razor" source="~/../blazor-samples/5.0/BlazorSample_WebAssembly/Pages/call-dotnet-from-js/CallDotNetExample6.razor"::: :::moniker-end :::moniker range="< aspnetcore-5.0" `CallDotNetExample6.razor`: :::code language="razor" source="~/../blazor-samples/3.1/BlazorSample_WebAssembly/Pages/call-dotnet-from-js/CallDotNetExample6.razor"::: :::moniker-end The following image shows the rendered parent component after the second **`InteropCall`** button is selected: * The second `ListItem1` component has displayed the `UpdateMessage Called!` message. * The **`InteropCall`** button for the second `ListItem1` component isn't visible because the button's CSS `display` property is set to `none`. ![Rendered 'CallDotNetExample6' component example](~/blazor/javascript-interoperability/call-dotnet-from-javascript/_static/component-example-6.png) ## Component instance .NET method called from `DotNetObjectReference` assigned to an element property The assignment of a to a property of an HTML element permits calling .NET methods on a component instance: * An [element reference](xref:blazor/js-interop/call-javascript-from-dotnet#capture-references-to-elements) is captured (). * In the component's [`OnAfterRender{Async}` method](xref:blazor/components/lifecycle#after-component-render-onafterrenderasync), a JavaScript (JS) function is invoked with the element reference and the component instance as a . The JS function attaches the to the element in a property. * When an element event is invoked in JS (for example, `onclick`), the element's attached is used to call a .NET method. Similar to the approach described in the [Component instance .NET method helper class](#component-instance-net-method-helper-class) section, this approach is useful in scenarios where using static .NET methods aren't applicable: * When several components of the same type are rendered on the same page. * In server-side apps with multiple users concurrently using the same component. * The .NET method is invoked from a JS event (for example, `onclick`), not from a Blazor event (for example, `@onclick`). In the following example: * The component contains several `ListItem2` components, which is a shared component. * Each `ListItem2` component is composed of a list item message `` and a second `` with a `display` CSS property set to `inline-block` for display. * When a `ListItem2` component list item is selected, that `ListItem2`'s `UpdateMessage` method changes the list item text in the first `` and hides the second `` by setting its `display` property to `none`. The following `assignDotNetHelper` JS function assigns the to an element in a property named `dotNetHelper`. The following `interopCall` JS function uses the for the passed element to invoke a .NET method named `UpdateMessage`. :::moniker range=">= aspnetcore-9.0" `ListItem2.razor.js`: :::code language="javascript" source="~/../blazor-samples/9.0/BlazorSample_BlazorWebApp/Components/ListItem2.razor.js"::: :::moniker-end :::moniker range=">= aspnetcore-8.0 < aspnetcore-9.0" `ListItem2.razor.js`: :::code language="javascript" source="~/../blazor-samples/8.0/BlazorSample_BlazorWebApp/Components/ListItem2.razor.js"::: :::moniker-end :::moniker range="< aspnetcore-8.0" ```html ``` :::moniker-end > [!NOTE] > For general guidance on JS location and our recommendations for production apps, see . In the preceding example, the variable name `dotNetHelper` is arbitrary and can be changed to any preferred name. The following `ListItem2` component is a shared component that can be used any number of times in a parent component and creates list items (`
  • ...
  • `) for an HTML list (`
      ...
    ` or `
      ...
    `). Each `ListItem2` component instance invokes the `assignDotNetHelper` JS function in [`OnAfterRenderAsync`](xref:blazor/components/lifecycle#after-component-render-onafterrenderasync) with an element reference (the first `` element of the list item) and the component instance as a . When a `ListItem2` component's message `` is selected, `interopCall` is invoked passing the `` element as a parameter (`this`), which invokes the `UpdateMessage` .NET method. In `UpdateMessage`, [`StateHasChanged`](xref:blazor/components/lifecycle#state-changes-statehaschanged) is called to update the UI when `message` is set and the `display` property of the second `` is updated. If `StateHasChanged` isn't called, Blazor has no way of knowing that the UI should be updated when the method is invoked. The is disposed when the component is disposed. `ListItem2.razor`: :::moniker range=">= aspnetcore-9.0" :::code language="razor" source="~/../blazor-samples/9.0/BlazorSample_BlazorWebApp/Components/ListItem2.razor"::: :::moniker-end :::moniker range=">= aspnetcore-8.0 < aspnetcore-9.0" :::code language="razor" source="~/../blazor-samples/8.0/BlazorSample_BlazorWebApp/Components/ListItem2.razor"::: :::moniker-end :::moniker range=">= aspnetcore-7.0 < aspnetcore-8.0" :::code language="razor" source="~/../blazor-samples/7.0/BlazorSample_WebAssembly/Shared/call-dotnet-from-js/ListItem2.razor"::: :::moniker-end :::moniker range=">= aspnetcore-6.0 < aspnetcore-7.0" :::code language="razor" source="~/../blazor-samples/6.0/BlazorSample_WebAssembly/Shared/call-dotnet-from-js/ListItem2.razor"::: :::moniker-end :::moniker range=">= aspnetcore-5.0 < aspnetcore-6.0" :::code language="razor" source="~/../blazor-samples/5.0/BlazorSample_WebAssembly/Shared/call-dotnet-from-js/ListItem2.razor"::: :::moniker-end :::moniker range="< aspnetcore-5.0" :::code language="razor" source="~/../blazor-samples/3.1/BlazorSample_WebAssembly/Shared/call-dotnet-from-js/ListItem2.razor"::: :::moniker-end The following parent component includes four list items, each an instance of the `ListItem2` component. :::moniker range=">= aspnetcore-9.0" `CallDotnet7.razor`: :::code language="razor" source="~/../blazor-samples/9.0/BlazorSample_BlazorWebApp/Components/Pages/CallDotnet7.razor"::: :::moniker-end :::moniker range=">= aspnetcore-8.0 < aspnetcore-9.0" `CallDotnet7.razor`: :::code language="razor" source="~/../blazor-samples/8.0/BlazorSample_BlazorWebApp/Components/Pages/CallDotnet7.razor"::: :::moniker-end :::moniker range=">= aspnetcore-7.0 < aspnetcore-8.0" `CallDotNetExample7.razor`: :::code language="razor" source="~/../blazor-samples/7.0/BlazorSample_WebAssembly/Pages/call-dotnet-from-js/CallDotNetExample7.razor"::: :::moniker-end :::moniker range=">= aspnetcore-6.0 < aspnetcore-7.0" `CallDotNetExample7.razor`: :::code language="razor" source="~/../blazor-samples/6.0/BlazorSample_WebAssembly/Pages/call-dotnet-from-js/CallDotNetExample7.razor"::: :::moniker-end :::moniker range=">= aspnetcore-5.0 < aspnetcore-6.0" `CallDotNetExample7.razor`: :::code language="razor" source="~/../blazor-samples/5.0/BlazorSample_WebAssembly/Pages/call-dotnet-from-js/CallDotNetExample7.razor"::: :::moniker-end :::moniker range="< aspnetcore-5.0" `CallDotNetExample7.razor`: :::code language="razor" source="~/../blazor-samples/3.1/BlazorSample_WebAssembly/Pages/call-dotnet-from-js/CallDotNetExample7.razor"::: :::moniker-end :::moniker range=">= aspnetcore-6.0" ## Synchronous JS interop in client-side components [!INCLUDE[](~/blazor/includes/js-interop/synchronous-js-interop-call-dotnet.md)] :::moniker-end ## JavaScript location Load JavaScript (JS) code using any of approaches described by the [article on JavaScript location](xref:blazor/js-interop/javascript-location): :::moniker range=">= aspnetcore-6.0" * [Load a script in `` markup](xref:blazor/js-interop/javascript-location#load-a-script-in-head-markup) (*Not generally recommended*) * [Load a script in `` markup](xref:blazor/js-interop/javascript-location#load-a-script-in-body-markup) * [Load a script from an external JavaScript file (`.js`) collocated with a component](xref:blazor/js-interop/javascript-location#load-a-script-from-an-external-javascript-file-js-collocated-with-a-component) * [Load a script from an external JavaScript file (`.js`)](xref:blazor/js-interop/javascript-location#load-a-script-from-an-external-javascript-file-js) * [Inject a script before or after Blazor starts](xref:blazor/js-interop/javascript-location#inject-a-script-before-or-after-blazor-starts) :::moniker-end :::moniker range="< aspnetcore-6.0" * [Load a script in `` markup](xref:blazor/js-interop/javascript-location#load-a-script-in-head-markup) (*Not generally recommended*) * [Load a script in `` markup](xref:blazor/js-interop/javascript-location#load-a-script-in-body-markup) * [Load a script from an external JavaScript file (`.js`)](xref:blazor/js-interop/javascript-location#load-a-script-from-an-external-javascript-file-js) * [Inject a script before or after Blazor starts](xref:blazor/js-interop/javascript-location#inject-a-script-before-or-after-blazor-starts) :::moniker-end :::moniker range=">= aspnetcore-5.0" Using JS modules to load JS is described in this article in the [JavaScript isolation in JavaScript modules](#javascript-isolation-in-javascript-modules) section. :::moniker-end :::moniker range=">= aspnetcore-8.0" > [!WARNING] > Only place a ` ``` :::moniker-end :::moniker range=">= aspnetcore-6.0" > [!NOTE] > For general guidance on JS location and our recommendations for production apps, see . :::moniker-end :::moniker range=">= aspnetcore-9.0" `CallDotnet8.razor`: :::code language="razor" source="~/../blazor-samples/9.0/BlazorSample_BlazorWebApp/Components/Pages/CallDotnet8.razor"::: :::moniker-end :::moniker range=">= aspnetcore-8.0 < aspnetcore-9.0" `CallDotnet8.razor`: :::code language="razor" source="~/../blazor-samples/8.0/BlazorSample_BlazorWebApp/Components/Pages/CallDotnet8.razor"::: :::moniker-end :::moniker range=">= aspnetcore-7.0 < aspnetcore-8.0" `CallDotNetExample8.razor`: :::code language="razor" source="~/../blazor-samples/7.0/BlazorSample_WebAssembly/Pages/call-dotnet-from-js/CallDotnetExample8.razor"::: :::moniker-end :::moniker range=">= aspnetcore-6.0 < aspnetcore-7.0" `CallDotNetExample8.razor`: :::code language="razor" source="~/../blazor-samples/6.0/BlazorSample_WebAssembly/Pages/call-dotnet-from-js/CallDotnetExample8.razor"::: :::moniker-end :::moniker range=">= aspnetcore-6.0" For information on using a byte array when calling JavaScript from .NET, see . :::moniker-end :::moniker range=">= aspnetcore-6.0" ## Stream from JavaScript to .NET Blazor supports streaming data directly from JavaScript to .NET. Streams are requested using the `Microsoft.JSInterop.IJSStreamReference` interface. `Microsoft.JSInterop.IJSStreamReference.OpenReadStreamAsync` returns a and uses the following parameters: * `maxAllowedSize`: Maximum number of bytes permitted for the read operation from JavaScript, which defaults to 512,000 bytes if not specified. * `cancellationToken`: A for cancelling the read. In JavaScript: ```javascript function streamToDotNet() { return new Uint8Array(10000000); } ``` In C# code: ```csharp var dataReference = await JS.InvokeAsync("streamToDotNet"); using var dataReferenceStream = await dataReference.OpenReadStreamAsync(maxAllowedSize: 10_000_000); var outputPath = Path.Combine(Path.GetTempPath(), "file.txt"); using var outputFileStream = File.OpenWrite(outputPath); await dataReferenceStream.CopyToAsync(outputFileStream); ``` In the preceding example: * `JS` is an injected instance. is registered by the Blazor framework. * The `dataReferenceStream` is written to disk (`file.txt`) at the current user's temporary folder path (). covers the reverse operation, streaming from .NET to JavaScript using a . covers how to upload a file in Blazor. For a forms example that streams `