Skills

Use Js Interop

Add, review, or fix JavaScript interop in Blazor components.

Install

npx skills add dotnet/skills --skill use-js-interop -g -a claude-code -y
Repository
dotnet/skills5.8k
Category
Engineering
Updated
3d ago
License
MIT
Context cost
Mediumabout 2,590 tokens
Runs scripts
No, instructions only

What it does

Add, review, or fix JavaScript interop in Blazor components. USE FOR: calling JavaScript from Blazor, calling .NET from JavaScript, collocated .razor.js modules, IJSRuntime, IJSObjectReference lifecycle, DotNetObjectReference, ElementReference, timing rules for when JS is available, IAsyncDisposable disposal of JS references, server-side JS interop safety. DO NOT USE FOR: general Blazor component authoring without JS interop needs (use author-component), forms (use collect-user-input).

JS Interop in Blazor

1. Collocated JS Modules

Always use collocated .razor.js files with export, never global window.* functions or <script> tags.

// ChartPanel.razor.js, placed next to ChartPanel.razor
export function initialize(canvas, dotNetRef) { /* ... */ }
export function updateData(points) { /* ... */ }
export function dispose() { /* ... */ }

Import paths: same project = "./Components/ChartPanel.razor.js", RCL = "./_content/{AssemblyName}/...".

2. Lifecycle Timing

All JS interop must happen in OnAfterRenderAsync or event handlers, never in OnInitialized, OnParametersSet, or constructors. JS is not available during server prerendering.

Use a typed interop wrapper (see Section 4), never call InvokeAsync/InvokeVoidAsync with raw string literals:

private ChartInterop? _chart;

protected override async Task OnAfterRenderAsync(bool firstRender)
{
    if (firstRender)
    {
        _chart = new ChartInterop(JS);
        await _chart.InitializeAsync(_canvasRef);
    }
}

Parameter changes: set a flag in OnParametersSet, apply in OnAfterRenderAsync:

private bool _dataChanged;

protected override void OnParametersSet() => _dataChanged = true;

protected override async Task OnAfterRenderAsync(bool firstRender)
{
    if (firstRender) { /* init */ }
    else if (_dataChanged && _chart is not null)
    {
        _dataChanged = false;
        await _chart.UpdateDataAsync(DataPoints);
    }
}

3. Batch Related Operations

Each JS interop call crosses the .NET-to-JS boundary (and in Blazor Server, the SignalR circuit). Batching applies in both directions, .NET→JS and JS→.NET.

.NET → JS: merge consecutive calls

If the C# side makes two or more JS calls in a row, combine them into one JS function:

Read the whole SKILL.md on GitHub

More in Engineering