C# and .NET Developer
Jeffallan/claude-skills
Guides C# work on .NET 8+: ASP.NET Core APIs, Entity Framework Core data access, Blazor apps and CQRS with MediatR, following a five-step build workflow.
Add, review, or fix JavaScript interop in Blazor components.
$ npx skills add dotnet/skills --skill use-js-interop -a claude-codeProject install by default; add -g for ~/.claude/skills/.
$ gh skill install dotnet/skills use-js-interop --agent claude-codeProject scope by default; add --scope user for a personal install. Needs GitHub CLI 2.90.0 or later (public preview).
$ git clone --depth 1 https://github.com/dotnet/skills.git skills-src && mkdir -p .claude/skills && cp -r skills-src/plugins/dotnet-blazor/skills/use-js-interop .claude/skills/use-js-interop && rm -rf skills-srcUse ~/.claude/skills/ instead of .claude/skills for a personal install. The folder must contain SKILL.md.
Claude Code skills documentation · loads skills from .claude/skills/
Install the "use-js-interop" agent skill from https://github.com/dotnet/skills/tree/main/plugins/dotnet-blazor/skills/use-js-interop into .claude/skills/use-js-interop/ in this project. Copy the whole folder (SKILL.md and every file beside it), keep the folder name "use-js-interop", then confirm the skill loads.Claude Code copies the folder itself, the same result as the manual copy. Check what it changed before you commit it.
$skill-installer install https://github.com/dotnet/skills/tree/main/plugins/dotnet-blazor/skills/use-js-interopType this inside Codex. $skill-installer <name> installs a curated skill from openai/skills. The installer writes to $CODEX_HOME/skills (default ~/.codex/skills). Restart Codex if the skill does not show up.
$ npx skills add dotnet/skills --skill use-js-interop -a codexProject install goes to .agents/skills/; add -g for ~/.codex/skills/.
$ gh skill install dotnet/skills use-js-interop --agent codexProject scope by default (.agents/skills/); add --scope user for a personal install.
$ git clone --depth 1 https://github.com/dotnet/skills.git skills-src && mkdir -p .agents/skills && cp -r skills-src/plugins/dotnet-blazor/skills/use-js-interop .agents/skills/use-js-interop && rm -rf skills-srcUse ~/.agents/skills/ instead of .agents/skills for a personal install.
Codex skills documentation · loads skills from .agents/skills/
Install the "use-js-interop" agent skill from https://github.com/dotnet/skills/tree/main/plugins/dotnet-blazor/skills/use-js-interop into .agents/skills/use-js-interop/ in this project. Copy the whole folder (SKILL.md and every file beside it), keep the folder name "use-js-interop", then confirm the skill loads.Codex copies the folder itself, the same result as the manual copy. Check what it changed before you commit it.
$ npx skills add dotnet/skills --skill use-js-interop -a cursorProject install goes to .agents/skills/; add -g for ~/.cursor/skills/.
$ gh skill install dotnet/skills use-js-interop --agent cursorProject scope by default (.agents/skills/); add --scope user for a personal install.
$ git clone --depth 1 https://github.com/dotnet/skills.git skills-src && mkdir -p .cursor/skills && cp -r skills-src/plugins/dotnet-blazor/skills/use-js-interop .cursor/skills/use-js-interop && rm -rf skills-srcUse ~/.cursor/skills/ instead of .cursor/skills for a personal install.
Cursor skills documentation · loads skills from .cursor/skills/, .agents/skills/, .claude/skills/, .codex/skills/
Install the "use-js-interop" agent skill from https://github.com/dotnet/skills/tree/main/plugins/dotnet-blazor/skills/use-js-interop into .cursor/skills/use-js-interop/ in this project. Copy the whole folder (SKILL.md and every file beside it), keep the folder name "use-js-interop", then confirm the skill loads.Cursor copies the folder itself, the same result as the manual copy. Check what it changed before you commit it.
$ gemini skills install https://github.com/dotnet/skills.git --path plugins/dotnet-blazor/skills/use-js-interop--scope user (default) or --scope workspace; --path is the subfolder of the repo that holds the skill; --consent skips the security confirmation prompt.
$ npx skills add dotnet/skills --skill use-js-interop -a gemini-cliProject install goes to .agents/skills/; add -g for ~/.gemini/skills/.
$ gh skill install dotnet/skills use-js-interop --agent gemini-cliProject scope by default (.agents/skills/); add --scope user for a personal install.
$ git clone --depth 1 https://github.com/dotnet/skills.git skills-src && mkdir -p .gemini/skills && cp -r skills-src/plugins/dotnet-blazor/skills/use-js-interop .gemini/skills/use-js-interop && rm -rf skills-srcUse ~/.gemini/skills/ instead of .gemini/skills for a personal install, then run /skills reload.
Gemini CLI skills documentation · loads skills from .gemini/skills/, .agents/skills/
Install the "use-js-interop" agent skill from https://github.com/dotnet/skills/tree/main/plugins/dotnet-blazor/skills/use-js-interop into .gemini/skills/use-js-interop/ in this project. Copy the whole folder (SKILL.md and every file beside it), keep the folder name "use-js-interop", then confirm the skill loads.Gemini CLI copies the folder itself, the same result as the manual copy. Check what it changed before you commit it.
$ gh skill install dotnet/skills use-js-interopInstalls for Copilot at project scope by default; add --scope user for a personal install. Preview a skill first with gh skill preview. Needs GitHub CLI 2.90.0 or later (public preview).
$ npx skills add dotnet/skills --skill use-js-interop -a github-copilotProject install goes to .agents/skills/; add -g for ~/.copilot/skills/.
$ git clone --depth 1 https://github.com/dotnet/skills.git skills-src && mkdir -p .github/skills && cp -r skills-src/plugins/dotnet-blazor/skills/use-js-interop .github/skills/use-js-interop && rm -rf skills-srcUse ~/.copilot/skills/ instead of .github/skills for a personal install. Commit .github/skills so cloud agent and code review can use it.
GitHub Copilot skills documentation · loads skills from .github/skills/, .claude/skills/, .agents/skills/
Install the "use-js-interop" agent skill from https://github.com/dotnet/skills/tree/main/plugins/dotnet-blazor/skills/use-js-interop into .github/skills/use-js-interop/ in this project. Copy the whole folder (SKILL.md and every file beside it), keep the folder name "use-js-interop", then confirm the skill loads.GitHub Copilot copies the folder itself, the same result as the manual copy. Check what it changed before you commit it.
$ npx skills add dotnet/skills --skill use-js-interop -a opencodeOpenCode documents no install command of its own. Project install goes to .agents/skills/; add -g for ~/.config/opencode/skills/.
$ gh skill install dotnet/skills use-js-interop --agent opencodeProject scope by default (.agents/skills/); add --scope user for a personal install.
$ git clone --depth 1 https://github.com/dotnet/skills.git skills-src && mkdir -p .opencode/skills && cp -r skills-src/plugins/dotnet-blazor/skills/use-js-interop .opencode/skills/use-js-interop && rm -rf skills-srcUse ~/.config/opencode/skills/ instead of .opencode/skills for a personal install.
OpenCode skills documentation · loads skills from .opencode/skills/, .claude/skills/, .agents/skills/
Install the "use-js-interop" agent skill from https://github.com/dotnet/skills/tree/main/plugins/dotnet-blazor/skills/use-js-interop into .opencode/skills/use-js-interop/ in this project. Copy the whole folder (SKILL.md and every file beside it), keep the folder name "use-js-interop", then confirm the skill loads.OpenCode copies the folder itself, the same result as the manual copy. Check what it changed before you commit it.
use-js-interopAdd, review, or fix JavaScript interop in Blazor components.
Use JS Interop is an agent skill from dotnet/skills, published by the product's own GitHub organization. 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).
Its SKILL.md is about 2.7k tokens, which your agent loads only when the skill is triggered. It is a single SKILL.md file with no bundled scripts.
It sits in Backend & APIs, covering Backend development. It works with Blazor, .NET, JavaScript and C#. The repository describes itself as: Repository for skills to assist AI coding agents with .NET and C. The licence is MIT.
7 steps, taken from the step headings in SKILL.md.
Read from SKILL.md and the folder at commit 8d670fa. It shows what the files ask for, not the result of running them.
Pre-approves nothing: there is no allowed-tools line, so your agent's usual permission prompts apply.
From allowed-tools in the SKILL.md frontmatter.
No scripts in the folder and no shell commands in SKILL.md (its code samples are csharp, javascript and razor).
From the folder's file list and the shell code blocks in SKILL.md.
No URLs in SKILL.md.
From URLs in SKILL.md, links to its own repository left out.
Names no API keys, tokens, secrets or passwords.
From names ending in _API_KEY, _TOKEN, _SECRET, _KEY or _PASSWORD in SKILL.md.
Use JS Interop loads about 2.7k tokens when it runs. Until then it costs about 126 tokens; SKILL.md has 622 words of instructions outside code blocks.
Estimates: characters ÷ 4, the usual rule of thumb; real counts depend on the model's tokenizer. Scripts and assets cost tokens only if the agent reads them.
The automated check found no risky patterns in SKILL.md.
Automated static check — not a guarantee. Review scripts before installing. It scans the text of SKILL.md for risky patterns (piping downloads into a shell, reading credential files, hidden Unicode, destructive commands); files beside SKILL.md are not scanned.
The full file from dotnet/skills at commit 8d670fa, republished under its MIT licence (© dotnet). 622 words, ~2,731 tokens.
.claude/skills/use-js-interop/SKILL.md (or your agent's skills folder).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}/...".
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);
}
}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.
If the C# side makes two or more JS calls in a row, combine them into one JS function:
// ❌ Two round-trips — theme and locale are always applied together
await _module.InvokeVoidAsync("applyTheme", theme);
await _module.InvokeVoidAsync("applyLocale", locale);
// ❌ Result of one call feeds into another — both can stay in JS
var token = await _module.InvokeAsync<string>("createAccessToken");
await _module.InvokeVoidAsync("storeToken", token);// ✅ One call applies both — no data dependency, no reason for two trips
export function applyPreferences(theme, locale) {
document.documentElement.dataset.theme = theme;
document.documentElement.lang = locale;
}
// ✅ Chain stays in JS — the token never needs to cross the boundary
export function createAndStoreToken() {
const token = crypto.randomUUID();
sessionStorage.setItem('access-token', token);
return token;
}When JS needs to send multiple pieces of data back to .NET, send them in a single invokeMethodAsync call rather than making separate callbacks:
// ❌ Two .NET round-trips from JS
await dotNetRef.invokeMethodAsync(ON_VOLUME_CHANGED, volume);
await dotNetRef.invokeMethodAsync(ON_PLAYBACK_CHANGED, isPlaying);
// ✅ One callback with all data
await dotNetRef.invokeMethodAsync(ON_PLAYER_STATE_CHANGED, { volume, isPlaying });Rule: if two interop calls always happen together from either side, merge them into one function.
Encapsulate interop for a feature in a plain class that owns the module lifecycle:
public sealed class ChartInterop : IAsyncDisposable
{
internal const string ModulePath = "./Components/ChartPanel.razor.js";
internal const string InitMethod = "initialize";
internal const string UpdateMethod = "updateData";
internal const string DisposeMethod = "dispose";
private readonly IJSRuntime _js;
private IJSObjectReference? _module;
public ChartInterop(IJSRuntime js) => _js = js;
private async ValueTask<IJSObjectReference> GetModuleAsync()
=> _module ??= await _js.InvokeAsync<IJSObjectReference>("import", ModulePath);
public async ValueTask InitializeAsync(ElementReference canvas)
{
var module = await GetModuleAsync();
await module.InvokeVoidAsync(InitMethod, canvas);
}
public async ValueTask UpdateDataAsync(IReadOnlyList<DataPoint> points)
{
var module = await GetModuleAsync();
await module.InvokeVoidAsync(UpdateMethod, points);
}
public async ValueTask DisposeAsync()
{
try
{
if (_module is not null)
{
await _module.InvokeVoidAsync(DisposeMethod);
await _module.DisposeAsync();
}
}
catch (JSDisconnectedException) { }
}
}The component creates and uses the wrapper with no magic strings:
@inject IJSRuntime JS
@implements IAsyncDisposable
<canvas @ref="_canvasRef" width="600" height="400"></canvas>
@code {
private ElementReference _canvasRef;
private ChartInterop? _chart;
protected override async Task OnAfterRenderAsync(bool firstRender)
{
if (firstRender)
{
_chart = new ChartInterop(JS);
await _chart.InitializeAsync(_canvasRef);
}
}
async ValueTask IAsyncDisposable.DisposeAsync()
{
if (_chart is not null)
await _chart.DisposeAsync();
}
}Prefer a concrete class over interface + implementation for interop wrappers. For unit testing, substitute IJSRuntime directly (it is already an interface).
_dotNetRef = DotNetObjectReference.Create(this);
await _module.InvokeVoidAsync("initialize", _dotNetRef);On the JS side, wrap the dotNetRef in a class. Use async/await with try/catch (not .catch()) to guard against circuit loss. Define .NET method name constants at the top:
const ON_CLIPBOARD_CHANGED = 'OnClipboardChanged';
class ClipboardMonitor {
#dotNetRef;
#abortController;
constructor(dotNetRef) {
this.#dotNetRef = dotNetRef;
this.#abortController = new AbortController();
}
start() {
document.addEventListener('copy', async () => {
try {
const text = await navigator.clipboard.readText();
await this.#dotNetRef.invokeMethodAsync(ON_CLIPBOARD_CHANGED, text);
} catch { /* circuit disconnected or clipboard denied */ }
}, { signal: this.#abortController.signal });
}
dispose() {
this.#abortController.abort();
}
}
let monitor;
export function initialize(dotNetRef) {
monitor = new ClipboardMonitor(dotNetRef);
monitor.start();
}
export function dispose() {
monitor?.dispose();
}Rules:
[JSInvokable] methods must be public — private/internal silently fails at runtimeStateHasChanged in InvokeAsync inside [JSInvokable] callbacks:[JSInvokable]
public async Task OnClipboardChanged(string text)
{
await InvokeAsync(() => { _lastClipboard = text; StateHasChanged(); });
}try/catch around invokeMethodAsync in JS — circuit loss throwsconst for .NET method name strings in JS — prevents typo bugs that silently failDotNetObjectReference in DisposeAsyncAlways implement IAsyncDisposable. Call JS cleanup first, then dispose references. Catch JSDisconnectedException for Blazor Server circuit loss:
public async ValueTask DisposeAsync()
{
try
{
if (_module is not null)
{
await _module.InvokeVoidAsync("dispose");
await _module.DisposeAsync();
}
}
catch (JSDisconnectedException) { }
_dotNetRef?.Dispose();
}Never use sync IDisposable for JS interop cleanup — InvokeVoidAsync returns ValueTask and must be awaited.
Pass DOM elements via @ref, not string IDs:
<canvas @ref="_canvasRef" width="600" height="400"></canvas>await _chart.InitializeAsync(_canvasRef);.razor.js with export — no window.* globalsOnAfterRenderAsync or event handlers — never during prerenderIAsyncDisposable catches JSDisconnectedExceptionDotNetObjectReference disposed in DisposeAsync; JS side has try/catch around invokeMethodAsync[JSInvokable] methods are public and use await InvokeAsync(StateHasChanged)InvokeVoidAsync used when no return value is neededElementReference instead of string IDs| Mistake | Fix |
|---|---|
| Using JS for something achievable with CSS | Use CSS custom properties, data- attributes, pseudo-classes |
| Many fine-grained interop calls | Batch into coarse functions — both .NET→JS and JS→.NET |
| Component imports JS module directly | Encapsulate in a strongly typed interop class |
| Magic strings for method names / module paths | Define internal const fields in the interop class |
| Interface + implementation for interop wrapper | Use a plain class; mock IJSRuntime for tests instead |
JS calls in OnInitializedAsync | Move to OnAfterRenderAsync(firstRender) |
InvokeAsync<object> for void calls | Use InvokeVoidAsync |
IDisposable with fire-and-forget JS | Use IAsyncDisposable with await |
Global window.* JS functions | Use collocated .razor.js with export |
| String element IDs passed to JS | Use ElementReference with @ref |
[JSInvokable] on private method | Must be public — silently fails otherwise |
DotNetObjectReference not disposed | Dispose in DisposeAsync — causes memory leak |
StateHasChanged() without InvokeAsync | Wrap in await InvokeAsync(() => { StateHasChanged(); }) |
JS invokeMethodAsync without error handling | Wrap in try/catch — circuit loss throws |
Bare dotNetRef in JS event handlers | Wrap in a class with #dotNetRef private field |
Magic strings in JS invokeMethodAsync calls | Use const at module top — typos silently fail at runtime |
JS calls in OnParametersSetAsync | Track changes, apply in OnAfterRenderAsync with guard |
| No null check before calling module | Check module is not null before use |
© dotnet, MIT. Rendered from Markdown: HTML in the file is shown as text, images as links, and headings moved down two levels. Raw file
Just SKILL.md in plugins/dotnet-blazor/skills/use-js-interop of dotnet/skills.
Open the folder on GitHubat commit 8d670fa
We found 2 copies of this SKILL.md (exact, near-identical or edited) in other folders, from 1 other GitHub owner. This page covers the copy in dotnet/skills, which our catalogue first saw on October 7, 2026.
Use JS Interop next to the 5 skills that share the most tags, products or categories with it. Stars are the repository's; “used in” counts other GitHub owners with a copy.
| Skill | Stars | Used in | Tokens | Auto-check | Licence | Repo updated |
|---|---|---|---|---|---|---|
| Use JS Interop this skilldotnet/skills | 5.6k | 1 repos | ~2.7k | Automated safety check: Pass | MIT | |
| C# and .NET DeveloperJeffallan/claude-skills | 12k | — | ~1.3k | Automated safety check: Pass | MIT | |
| Blazormanagedcode/dotnet-skills | 486 | — | ~2.1k | Automated safety check: Pass | MIT | |
| Dotnet Backend Patternswshobson/agents | 40k | 7 repos | ~6.6k | Automated safety check: Pass | MIT | |
| Using Dotnetnovotnyllc/dotnet-artisan | 233 | — | ~2.3k | Automated safety check: Warn | MIT | |
| .NET Core ExpertJeffallan/claude-skills | 12k | — | ~1.3k | Automated safety check: Pass | MIT |
Jeffallan/claude-skills
Guides C# work on .NET 8+: ASP.NET Core APIs, Entity Framework Core data access, Blazor apps and CQRS with MediatR, following a five-step build workflow.
managedcode/dotnet-skills
Build and review Blazor applications across server, WebAssembly, web app, and hybrid scenarios with correct component design, state flow, rendering, and hosting choices.
wshobson/agents
Master C/.NET backend development patterns for building robust APIs, MCP servers, and enterprise applications.
novotnyllc/dotnet-artisan
Detects .NET intent for any C, ASP.NET Core, EF Core, Blazor, MAUI, Uno Platform, WPF, WinUI, SignalR, gRPC, xUnit, NuGet, or MSBuild request from prompt keywords and repository signals (.sln…
Jeffallan/claude-skills
Guides building .NET 8 services with minimal APIs, clean architecture, Entity Framework Core, MediatR CQRS and JWT authentication, with build and test checks.
Nethereum/Nethereum
Implement Sign-In with Ethereum (SIWE / EIP-4361) authentication in Blazor with JWT tokens, session management, and AuthorizeView (.NET/C).
dotnet/skills
Resolves .NET runtime frames in Apple .ips crash logs to function names, source files and line numbers using dSYM symbols, atos and the Microsoft symbol server.
dotnet/skills
Resolves native crash frames from .NET Android tombstones to function names, source files and line numbers using BuildIds, Microsoft's symbol server and llvm-symbolizer.
dotnet/skills
Scans C# and .NET code for about 50 performance anti-patterns and reports prioritized findings with concrete fixes, at a scan depth you choose.
dotnet/skills
Statically pairs source files with test files to list code that no test references, using Roslyn for C# or tree-sitter for many languages, with no build.
dotnet/skills
Activate this skill when BenchmarkDotNet (BDN) is involved in the task — creating, running, configuring, or reviewing BDN benchmarks.
dotnet/skills
Makes .NET projects compatible with Native AOT and trimming by resolving IL trim and AOT analyzer warnings through annotations rather than suppressions.
Works with
Categories
Add, review, or fix JavaScript interop in Blazor components. Use JS Interop is an agent skill from dotnet/skills, published by the product's own GitHub organization. Add, review, or fix JavaScript interop in Blazor components.
Use JS Interop fits situations like: : calling JavaScript from Blazor; calling .NET from JavaScript; collocated .razor.js modules; IJSObjectReference lifecycle.
Run `npx skills add dotnet/skills --skill use-js-interop -a claude-code`. Or copy the skill folder (plugins/dotnet-blazor/skills/use-js-interop in dotnet/skills) into .claude/skills/use-js-interop in your project. Claude Code loads it when a task matches its description.
Run `npx skills add dotnet/skills --skill use-js-interop -a codex`. Or copy the skill folder (plugins/dotnet-blazor/skills/use-js-interop in dotnet/skills) into .agents/skills/use-js-interop in your project. Codex loads it when a task matches its description.
Cursor, Gemini CLI, GitHub Copilot and OpenCode also load SKILL.md folders. With the skills CLI, run `npx skills add dotnet/skills --skill use-js-interop -a cursor` (or -a gemini-cli, github-copilot or opencode for the others). To copy it by hand, put the folder in .cursor/skills/use-js-interop, .gemini/skills/use-js-interop, .github/skills/use-js-interop and .opencode/skills/use-js-interop in your project.
SKILL.md names no scripts, command-line tools or credentials: Use JS Interop is instructions for the agent only.
SKILL.md contains no URLs. Any network use would come from the scripts or tools the agent runs. This is read from the text; nothing was executed.
Our automated static check of SKILL.md found no risky patterns, such as piping downloads into a shell, reading credential files or hidden Unicode. It is not a guarantee. Review the folder before installing.
Use JS Interop is published under the MIT licence (declared in SKILL.md). It allows redistribution, so the full SKILL.md is shown on this page.
About 2.7k tokens (SKILL.md is roughly 11k characters). Agents keep only the skill's name and description in context until a task matches; then they load SKILL.md in full.
Skills that share tags, products or a category with Use JS Interop: C# and .NET Developer (Jeffallan/claude-skills, 12k stars), Blazor (managedcode/dotnet-skills, 486 stars), Dotnet Backend Patterns (wshobson/agents, 40k stars) and Using Dotnet (novotnyllc/dotnet-artisan, 233 stars). The comparison table on this page puts their stars, adoption, token cost, safety result and licence side by side.
dotnet (a GitHub organization, an official publisher) maintains it in dotnet/skills, which has 5,568 GitHub stars. The repository holds 91 skills in this directory. The repository was last updated on October 7, 2026.
Source: dotnet/skills on GitHub. Facts on this page come from the repository at the commit we read; the author's words are quoted as theirs.