JavaScript interop in Blazor for calling JS from .NET and .NET from JS
From dotnet-blazornpx claudepluginhub markus41/claude --plugin dotnet-blazorThis skill is limited to using the following tools:
Guides Next.js Cache Components and Partial Prerendering (PPR) with cacheComponents enabled. Implements 'use cache', cacheLife(), cacheTag(), revalidateTag(), static/dynamic optimization, and cache debugging.
Migrates code, prompts, and API calls from Claude Sonnet 4.0/4.5 or Opus 4.1 to Opus 4.5, updating model strings on Anthropic, AWS, GCP, Azure platforms.
Analyzes BMad project state from catalog CSV, configs, artifacts, and query to recommend next skills or answer questions. Useful for help requests, 'what next', or starting BMad.
@inject IJSRuntime JS
@code {
private async Task ShowAlert()
{
await JS.InvokeVoidAsync("alert", "Hello from Blazor!");
}
private async Task<string> GetValue()
{
return await JS.InvokeAsync<string>("localStorage.getItem", "key");
}
// With element reference
private ElementReference _inputRef;
protected override async Task OnAfterRenderAsync(bool firstRender)
{
if (firstRender)
{
await JS.InvokeVoidAsync("focusElement", _inputRef);
}
}
}
// Static .NET method
[JSInvokable]
public static Task<string> GetDataFromDotNet()
{
return Task.FromResult("Hello from .NET!");
}
// Instance method
private DotNetObjectReference<MyComponent>? _dotNetRef;
protected override void OnInitialized()
{
_dotNetRef = DotNetObjectReference.Create(this);
}
[JSInvokable]
public void HandleJsCallback(string data)
{
_message = data;
StateHasChanged();
}
public void Dispose() => _dotNetRef?.Dispose();
// Call static method
const result = await DotNet.invokeMethodAsync('MyApp', 'GetDataFromDotNet');
// Call instance method
dotNetRef.invokeMethodAsync('HandleJsCallback', 'data from JS');
Components/Pages/
├── Map.razor
├── Map.razor.cs
└── Map.razor.js ← Collocated JS module
// Map.razor.js
export function initializeMap(element, options) {
const map = new google.maps.Map(element, options);
return map;
}
export function dispose() {
// Cleanup
}
// Map.razor.cs
private IJSObjectReference? _module;
protected override async Task OnAfterRenderAsync(bool firstRender)
{
if (firstRender)
{
_module = await JS.InvokeAsync<IJSObjectReference>(
"import", "./Components/Pages/Map.razor.js");
await _module.InvokeVoidAsync("initializeMap", _mapElement, _options);
}
}
public async ValueTask DisposeAsync()
{
if (_module is not null)
{
await _module.InvokeVoidAsync("dispose");
await _module.DisposeAsync();
}
}
.razor.js modules over global scriptsIJSObjectReference and DotNetObjectReferenceOnAfterRenderAsync (DOM must exist)IJSRuntime in Server mode, IJSInProcessRuntime only in WASMJSDisconnectedException in Blazor Server for graceful cleanup