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.1k96 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. 📊 Dashboard - Accuracy and efficiency scoring trends for contained plugins (

Get the whole plugin

Other skills on dotnet-skills.