AgentStack
Browse Sign in
Browse Why AgentStack Sell Docs
Sign in
SKILL verified MIT Self-run

Coordinate Components

skill-dotnet-skills-coordinate-components · by dotnet

>

No reviews yet
0 installs
5 views
0.0% view→install

Install

$ agentstack add skill-dotnet-skills-coordinate-components

✓ scanned · ✓ verified, works with Claude Code, Cursor, and more.

Security review

✓ Passed

No issues found. Passed automated security review. · v0.1.0 How review works →

  • Prompt-injection patterns
  • Secret / credential exfiltration
  • Dangerous shell & filesystem operations
  • Untrusted network calls
  • Known-malicious package signatures

What it can access

  • Network access No
  • Filesystem access No
  • Shell / process execution No
  • Environment & secrets No
  • Dynamic code execution No

From automated source analysis of v0.1.0. “Used” means the capability is present in the source — more access means more to trust, not that it’s unsafe.

View the full security report →

Verified badge

Passed review? Show it. Paste this badge into your README, it links to the public security report.

AgentStack Verified badge Links to your public security report.
[![AgentStack Verified](https://agentstack.voostack.com/badges/verified.svg)](https://agentstack.voostack.com/security/report/skill-dotnet-skills-coordinate-components)

Reliability & compatibility

Security review passed
0 installs to date
no reviews yet
1mo ago

Declared compatibility

Claude CodeClaude Desktop

Compatibility is declared by the source manifest. End-to-end runtime verification is coming, see below.

Preview Execution monitoring

We're building live execution health for every listing: tool-call success rate, median latency, uptime, and last-checked timestamps, measured, not self-reported. It isn't live yet, so we don't show numbers we can't stand behind.

How agent discovery & health will work →
Are you the author of Coordinate Components? Claim this listing to set pricing, connect Stripe payouts, and keep 70% of every sale.
Sign up to claim

About

Coordinate Components

Step 1 — Read AGENTS.md

Read AGENTS.md at the workspace root to learn the project's conventions before making changes.

Step 2 — Decide the scope

| Need | Mechanism | When to use | |------|-----------|-------------| | Subtree (same render mode) | CascadingValue component | Theme, layout config within a layout | | App-wide (all render modes) | CascadingValueSource via DI | Current user, feature flags, theme shared globally | | Mutable shared state within a circuit | Scoped service + Action event | Shopping cart, notification count, selected filters |

For parent→child one level: use [Parameter] / EventCallback (see author-component skill). For persisting state across prerender→interactive: see support-prerendering skill.

Workflow (quick reference)

  1. Choose the mechanism from the table in Step 2
  2. If crossing render mode boundaries → use CascadingValueSource (Step 4)
  3. Register in Program.cs with AddCascadingValue(...) and isFixed: false
  4. Consume via [CascadingParameter] in child components
  5. Update via NotifyChangedAsync(newValue) — never page reload
  6. For additional mutable state within a circuit → add scoped service (Step 5)
  7. Wrap any StateHasChanged from background threads in InvokeAsync
  8. Implement IDisposable — dispose timers, cancel tokens, unsubscribe events

Step 3 — CascadingValue for subtree state

Wrap a subtree with `` to flow data to all descendants without passing it through every intermediate component.

@* In a layout or parent component *@

    @Body

@code {
    private ThemeInfo theme = new() { ButtonClass = "btn-primary" };
}

Consume in any descendant:

[CascadingParameter]
private ThemeInfo? Theme { get; set; }

Rules:

  • Matched by type, not name. To cascade multiple values of the same type, add Name:

``razor ... ` `csharp [CascadingParameter(Name = "PrimaryTheme")] private ThemeInfo? Primary { get; set; } ``

  • Set IsFixed="true" when the value never changes — avoids subscription overhead.
  • Does NOT cross render mode boundaries. A `` in a static SSR parent is invisible to interactive children. See Step 6.

Step 4 — CascadingValueSource<T> for app-wide state

Register a CascadingValueSource in DI when the value must be available to all components regardless of render mode.

// Program.cs
builder.Services.AddCascadingValue(sp =>
{
    var theme = new ThemeInfo { ButtonClass = "btn-primary" };
    return new CascadingValueSource(theme, isFixed: false);
});

Consume identically to Step 3:

[CascadingParameter]
private ThemeInfo? Theme { get; set; }

To update and notify subscribers, either mutate the existing object or replace it:

@* Component that changes the theme *@
@inject CascadingValueSource ThemeSource

Toggle theme

@code {
    private bool isDark;

    private async Task ToggleDarkMode()
    {
        isDark = !isDark;
        // Replace the value entirely:
        var newTheme = new ThemeInfo { ButtonClass = isDark ? "btn-dark" : "btn-primary" };
        await ThemeSource.NotifyChangedAsync(newTheme);
    }
}

NotifyChangedAsync() (no argument) also works — mutate the object and then call it. NotifyChangedAsync(newValue) replaces the value and notifies in one step.

Update protocol: Whenever shared state changes, the component that changes it MUST inject CascadingValueSource and call NotifyChangedAsync(). This is the only mechanism that triggers re-rendering in all [CascadingParameter] subscribers. Without this call, no subscribers update. Do not use NavigationManager.Refresh() or page reloads as a substitute.

Rules:

  • isFixed: false enables change notifications. isFixed: true is better for truly static values (feature flags).
  • Crosses render mode boundaries — works for per-page interactivity, global interactivity, and WebAssembly. Key advantage over ``.
  • Keep cascaded types granular. Every NotifyChangedAsync re-renders ALL subscribers regardless of which property changed. Don't put all app state into one cascaded type.
  • For Auto/WebAssembly apps, register in both server and .Client Program.cs. The type must be in a shared assembly.

Step 5 — Scoped state service with change events

For mutable shared state that multiple components read and write (shopping cart, notification count, filters), use a scoped service with an event for change notification.

Define the service:

public class CartState
{
    private readonly List _items = [];

    public IReadOnlyList Items => _items;
    public int Count => _items.Count;

    public event Action? OnChange;

    public void Add(CartItem item)
    {
        _items.Add(item);
        OnChange?.Invoke();
    }

    public void Remove(CartItem item)
    {
        _items.Remove(item);
        OnChange?.Invoke();
    }
}

Register as scoped:

builder.Services.AddScoped();

Subscribe in components:

@inject CartState Cart
@implements IDisposable

@Cart.Count

@code {
    protected override void OnInitialized()
    {
        Cart.OnChange += StateHasChanged;
    }

    public void Dispose()
    {
        Cart.OnChange -= StateHasChanged;
    }
}

The simple Action OnChange pattern works when the event fires from the Blazor sync context (button click → Cart.Add(…)). If the event fires from outside the sync context (timer, background task, SignalR hub), wrap in InvokeAsync:

private Action? _handler;

protected override void OnInitialized()
{
    _handler = () => InvokeAsync(StateHasChanged);
    Cart.OnChange += _handler;
}

public void Dispose() => Cart.OnChange -= _handler;

Store the delegate in a field so you can unsubscribe the exact same instance.

Step 6 — Render mode and service lifetime rules

Cascading values don't cross render mode boundaries

A ` placed in a static SSR layout (MainLayout.razor when the layout renders statically) will **not** reach interactive children. The interactive component sees null` for the cascading parameter.

Fix: Use CascadingValueSource registered in DI (Step 4) or a scoped service (Step 5). Both cross boundaries because DI services are resolved per-circuit, not from the component tree.

Service lifetime on Server vs WebAssembly

| Lifetime | Server | WebAssembly | |----------|--------|-------------| | Scoped | Per circuit (per user connection) | Per browser tab | | Singleton | Shared across ALL users | Per browser tab (safe) | | Transient | New instance per injection | New instance per injection |

On Server, never store user-specific state in a singleton — every user's circuit shares the same singleton. One user's cart leaks into another's. Use AddScoped().

On WebAssembly, singletons are per-tab and safe. But code meant for both Server and WebAssembly (Auto mode) must use scoped.

Auto/WebAssembly with prerendering

State services must be defined in the .Client project or a shared assembly — they cannot reference server-only types. Register the service in both Program.cs files. State created during prerender does not survive the switch to the interactive runtime. Use the support-prerendering skill's [PersistentState] pattern to carry state across.

Don'ts

  • Don't use a singleton for per-user state on Server — all circuits share it, leaking state between users.
  • Don't put all app state into one cascaded objectNotifyChangedAsync re-renders ALL subscribers on every change. Separate concerns into distinct types (ThemeState, CartState, UserPreferences).
  • Don't forget to unsubscribe — omitting Dispose on event subscriptions causes memory leaks that grow per-circuit.
  • **Don't use ` in a static layout expecting it to reach interactive children** — it won't cross render mode boundaries. Use DI-registered CascadingValueSource` or scoped services.
  • Don't use NavigationManager.Refresh(forceReload: true) to propagate cascading value changes — this destroys the circuit and forces a full page reload. Instead, inject CascadingValueSource and call NotifyChangedAsync(newValue) to push updates to all [CascadingParameter] subscribers without a page reload.
  • Don't call StateHasChanged from a non-Blazor thread — wrap in InvokeAsync. The framework throws InvalidOperationException: The current thread is not associated with the Dispatcher.

Source & license

This open-source skill is cataloged on AgentStack and links to its original source — we do not rehost the code.

Install and usage instructions live in the source repository linked above.

Reviews

No reviews yet, be the first.

Versions

  • v0.1.0 Imported from the upstream source.