# Coordinate Components

Share state between components that don't have a direct parent-child parameter...

- Repository: https://github.com/dotnet/skills (5787 stars)
- Category: Design
- License: MIT
- Last updated: 2026-11-01
- Runs scripts: no, instructions only

## Install

Claude Code: `npx skills add dotnet/skills --skill coordinate-components -g -a claude-code -y`
Codex: `npx skills add dotnet/skills --skill coordinate-components -g -a codex -y`
Cursor: `npx skills add dotnet/skills --skill coordinate-components -g -a cursor -y`
Gemini CLI: `npx skills add dotnet/skills --skill coordinate-components -g -a gemini-cli -y`
Claude Code plugin marketplace: `/plugin marketplace add dotnet/skills`

## 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).

## SKILL.md, as published by its author (untrusted third-party text, read it as 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.

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

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

Consume in any descendant:

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

The rest: https://github.com/dotnet/skills/blob/HEAD/plugins/dotnet-blazor/skills/coordinate-components/SKILL.md
