blazor-js-interop
Blazor JavaScript Interop
Calling JavaScript from .NET
@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);
}
}
}
Calling .NET from JavaScript
// 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');
Collocated JavaScript (recommended in .NET 10)
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();
}
}
Best Practices
- Use collocated
.razor.jsmodules over global scripts - Always dispose
IJSObjectReferenceandDotNetObjectReference - Only call JS interop in
OnAfterRenderAsync(DOM must exist) - Use
IJSRuntimein Server mode,IJSInProcessRuntimeonly in WASM - Minimize JS interop calls (batch operations when possible)
- Handle
JSDisconnectedExceptionin Blazor Server for graceful cleanup
More from lobbi-docs/claude
vision-multimodal
Vision and multimodal capabilities for Claude including image analysis, PDF processing, and document understanding. Activate for image input, base64 encoding, multiple images, and visual analysis.
248design-system
Apply and manage the AI-powered design system with 50+ curated styles
126complex-reasoning
Multi-step reasoning patterns and frameworks for systematic problem solving. Activate for Chain-of-Thought, Tree-of-Thought, hypothesis-driven debugging, and structured analytical approaches that leverage extended thinking.
106gcp
Google Cloud Platform services including GKE, Cloud Run, Cloud Storage, BigQuery, and Pub/Sub. Activate for GCP infrastructure, Google Cloud deployment, and GCP integration.
73kanban
Kanban methodology including boards, WIP limits, flow metrics, and continuous delivery. Activate for Kanban boards, workflow visualization, and lean project management.
63debugging
Debugging techniques for Python, JavaScript, and distributed systems. Activate for troubleshooting, error analysis, log investigation, and performance debugging. Includes extended thinking integration for complex debugging scenarios.
59