← Back to list

blazor-components
by barracoder
⭐ 0🍴 0📅 Jan 25, 2026
SKILL.md
name: blazor-components description: Blazor-specific patterns for components, JS interop, and state management
blazor-components
Patterns for building Blazor WebAssembly components effectively.
JS Interop
Invoking JavaScript from C#:
await JSRuntime.InvokeVoidAsync("functionName", arg1, arg2);
var result = await JSRuntime.InvokeAsync<string>("functionName", arg1);
Receiving calls from JavaScript:
private DotNetObjectReference<MyComponent>? _dotNetRef;
protected override void OnInitialized()
{
_dotNetRef = DotNetObjectReference.Create(this);
}
[JSInvokable]
public void OnJsCallback(string data)
{
// Handle callback from JS
StateHasChanged();
}
Disposal Pattern
Always implement IAsyncDisposable for JS interop components:
@implements IAsyncDisposable
@code {
private DotNetObjectReference<GameCanvas>? _dotNetRef;
public async ValueTask DisposeAsync()
{
if (_dotNetRef is not null)
{
await JSRuntime.InvokeVoidAsync("cleanup", _dotNetRef);
_dotNetRef.Dispose();
}
}
}
State Management
UI updates after async/JS operations:
// From JS callback or async operation - must use InvokeAsync
await InvokeAsync(StateHasChanged);
// From synchronous event handler - direct call is fine
StateHasChanged();
Shared state via DI service:
// Services/GameState.cs
public class GameState
{
public int Score { get; set; }
public int Lives { get; set; }
public event Action? OnChange;
public void NotifyStateChanged() => OnChange?.Invoke();
}
// Program.cs
builder.Services.AddSingleton<GameState>();
// Component
@inject GameState State
@implements IDisposable
protected override void OnInitialized() => State.OnChange += StateHasChanged;
public void Dispose() => State.OnChange -= StateHasChanged;
Canvas Rendering
Use Blazor.Extensions.Canvas for 2D graphics:
@using Blazor.Extensions
@using Blazor.Extensions.Canvas.Canvas2D
<BECanvas Width="800" Height="600" @ref="_canvasRef" />
@code {
private BECanvasComponent? _canvasRef;
private Canvas2DContext? _context;
protected override async Task OnAfterRenderAsync(bool firstRender)
{
if (firstRender)
{
_context = await _canvasRef!.CreateCanvas2DAsync();
}
}
private async Task Render()
{
await _context!.ClearRectAsync(0, 0, 800, 600);
await _context.SetFillStyleAsync("green");
await _context.FillRectAsync(x, y, width, height);
}
}
Collision Detection
Axis-aligned bounding box (AABB) helper:
private static bool RectsIntersect(
double x1, double y1, double w1, double h1,
double x2, double y2, double w2, double h2)
{
return x1 < x2 + w2 && x1 + w1 > x2 &&
y1 < y2 + h2 && y1 + h1 > y2;
}
Game Loop Pattern
requestAnimationFrame via JS interop:
// wwwroot/js/gameLoop.js
let animationId = null;
window.startGameLoop = (dotNetRef) => {
const loop = (timestamp) => {
dotNetRef.invokeMethodAsync("OnFrame", timestamp);
animationId = requestAnimationFrame(loop);
};
animationId = requestAnimationFrame(loop);
};
window.stopGameLoop = () => {
if (animationId) cancelAnimationFrame(animationId);
};
[JSInvokable]
public async Task OnFrame(double timestamp)
{
Update();
await Render();
await InvokeAsync(StateHasChanged); // Sync Blazor UI
}
Common Gotchas
- Call
InvokeAsync(StateHasChanged)after canvas Render operations to sync Blazor markup - Blazor WASM is single-threaded—avoid blocking calls
- Use
@keydirective when rendering lists that change order - JS files must be in
wwwroot/and referenced inindex.html OnAfterRenderAsync(firstRender: true)is when DOM/canvas is ready
Score
Total Score
50/100
Based on repository quality metrics
✓SKILL.md
SKILL.mdファイルが含まれている
+20
○LICENSE
ライセンスが設定されている
0/10
○説明文
100文字以上の説明がある
0/10
○人気
GitHub Stars 100以上
0/15
○最近の活動
3ヶ月以内に更新がある
0/10
○フォーク
10回以上フォークされている
0/5
✓Issue管理
オープンIssueが50未満
+5
✓言語
プログラミング言語が設定されている
+5
○タグ
1つ以上のタグが設定されている
0/5
Reviews
💬
Reviews coming soon