Skip to content
Development
Skill

/coordinate-components

Share state between components that don't have a direct parent-child parameter relationship, using cascading values, scoped services with change events, or CascadingValueSource via DI. USE WHEN the user needs a CascadingParameter or CascadingValue that works across render mode

From plugin
dotnet-skills
5.4k98 skills16 agents
Install
$ npx -y skills add dotnet/skills --skill coordinate-components --agent claude-code

How it fires

How this skill gets triggered: by you, by Claude, or both.

  • Fires itselfAuto-invocation. Claude auto-loads it when your prompt matches the work.Auto-invocation is when the right skill fires by itself at the right moment, driven by a FLOW.md router and a hook, instead of you invoking it by name. It is the difference between a skill being installed and a skill actually getting used.Read the full definition →
  • You can call itInvoke it directly when you want it.
  • Slash command/coordinate-components

Context preview

The summary Claude sees to decide when to auto-load this skill.

Share state between components that don't have a direct parent-child parameter relationship, using cascading values, scoped services with change events, or CascadingValueSource via DI. USE WHEN the user needs a CascadingParameter or CascadingValue that works across render mode

SKILL.md

coordinate-components.SKILL.md
license: MIT
name: coordinate-components
description: >
  Share state between components that don't have a direct parent-child parameter relationship,
  using cascading values, scoped services with change events, or CascadingValueSource via DI.
  USE WHEN the user needs a CascadingParameter or CascadingValue that works across render mode
  boundaries, a shopping cart or notification count accessible from multiple pages, a theme or
  user preference cascaded app-wide, or when components in different parts of the tree must
  react when shared data changes. Also USE WHEN cascading values aren't reaching interactive
  children in per-page interactivity mode, or when the user needs to understand scoped vs
  singleton service lifetime for state on Blazor Server.
  DO NOT USE for direct parent-child parameter passing or EventCallback (see author-component),
  for persisting state across prerender-to-interactive transitions (see support-prerendering),
  or for service abstractions for data fetching in Auto/WebAssembly (see fetch-and-send-data).

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<T>` 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<T>` (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 `<CascadingValue>` to flow data to all descendants without passing it through every intermediate component.

@* In a layout or parent component *@
<CascadingValue Value="theme">
    @Body
</CascadingValue>

@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`:
  <CascadingValue Value="primary" Name="PrimaryTheme">...</CascadingValue>
  [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 `<CascadingValue>` in a static SSR parent is invisible to interactive children. See Step 6.

Step 4 — CascadingValueSource&lt;T&gt; for app-wide state

Register a `CascadingValueSource<T>` 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<ThemeInfo>(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<ThemeInfo> ThemeSource

<button @onclick="ToggleDarkMode">Toggle theme</button>

@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<T>` 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 `<CascadingValue>`.
  • 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<CartItem> _items = [];

    public IReadOnlyList<CartItem> Items => _items;
    public int
Read more
Ships withdotnet-skills

This repository contains the .NET team's curated set of core skills and custom agents for coding agents. For information about the Agent Skills standard, see agentskills.io.

Get the whole plugin

Other skills on dotnet-skills.