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:
