К основному содержанию
скилл CX Codex

coordinate-components — скилл для Claude Code: как ставить и что даёт

Скилл учит агента связывать состояние компонентов Blazor без цепочек параметров от предка к потомку. Помогает настроить каскадные значения и сервисы со scope.

Установить в Claude Code · Через Skills CLI (Vercel) Все способы и платформы ↓
npx skills add dotnet/skills --skill coordinate-components -a claude-code
Открыть на GitHub

Что делает coordinate-components

Скилл помогает агенту выстроить передачу состояния между компонентами Blazor, когда прямой связи через параметры нет. Сначала агент проверяет файл AGENTS.md в корне проекта, чтобы учесть локальные правила репозитория, а затем выбирает архитектурный вариант: разметку <CascadingValue>, регистрацию CascadingValueSource<T> через DI или scoped-сервис с событиями изменений.

Обычный <CascadingValue> из родительского компонента со статическим SSR не доходит до интерактивных дочерних узлов, поэтому попытка передать данные через разметку на границе режимов рендеринга тихо оставит потомков без контекста. Для таких стыков инструкция предписывает внедрять CascadingValueSource<T> с параметром isFixed: false и вызывать метод NotifyChangedAsync(), не прибегая к перезагрузке страницы.

Для часто меняющихся данных вроде корзины покупок или счётчика уведомлений скилл генерирует scoped-сервис с событием Action. В компонентах-подписчиках обязательно реализуется интерфейс IDisposable для отписки, а вызовы обновлений из фоновых потоков или таймеров оборачиваются в InvokeAsync.

Какие задачи решает

  • Связать общее состояние приложения между компонентами в разных режимах рендеринга Blazor
  • Организовать обновление счётчика корзины или уведомлений через scoped-сервис
  • Пробросить тему оформления в поддерево компонентов через CascadingValue
  • Настроить уведомления об изменении параметров без перезагрузки страниц

Примеры запросов агенту

  • Помоги пробросить текущую тему оформления через CascadingValueSource в Blazor
  • Создай scoped-сервис CartState с событием OnChange для счётчика корзины
  • Настрой передачу параметров пользователя через границы режимов рендеринга

Как установить coordinate-components

1Через Skills CLI (Vercel)

npx skills add dotnet/skills --skill coordinate-components -a claude-code

2Вручную: положить файлы

~/.claude/skills/coordinate-components/SKILL.md

Папка скилла целиком; для одного проекта — .claude/skills/coordinate-components/

По шагам, с командами для каждой платформы: Как установить скилл в Claude Code: пять способов

Из того же набора101

extension-points Скилл разбирает, откуда MSBuild берёт хуки и импорты: CustomBefore/CustomAfter, wildcard-каталоги, авто-импорты NuGet. Пригодится, когда ассеты пропали или подменились. CX Codex +1 3 000установок item-management Помогает настраивать элементы MSBuild и разбирать ошибки в ItemGroup. Скилл учит агента правилам для Include, Remove, Update, батчинга и трансформаций. CX Codex +1 3 000установок plan-ui-change Разбивает сложную страницу Blazor на отдельные компоненты до того, как агент начнёт писать код. Пригодится, когда в макете три и больше визуальных блоков. CX Codex +1 3 000установок collect-user-input Скилл для работы с формами в Blazor: EditForm, встроенные компоненты ввода, валидация и обработка отправки. Пригодится, когда нужно добавить форму, поиск или загрузку файлов. CX Codex +1 3 000установок create-blazor-project Скилл проводит агента через выбор режима рендеринга Blazor и запускает dotnet new blazor с нужными флагами. CX Codex +1 3 000установок property-patterns Разбирает условия, дефолты и перезаписи в свойствах MSBuild по шаблонам из репозитория MSBuild. Пригодится, когда сборка берёт не то значение или падает на чужой ОС. CX Codex +1 3 000установок author-component Помогает проектировать и проверять компоненты Blazor без вызовов JavaScript. Пригодится при настройке параметров, слотов RenderFragment и асинхронного жизненного цикла. CX Codex +1 3 100установок test-analysis-extensions Справочный каталог файлов с таблицами для анализа тестов: маркеры, ассерты, пропуски, паттерны ожиданий. Агент читает нужный файл под язык проекта. CX Codex +1 3 100установок

Похожие

blazor Mindrally Скилл задаёт агенту правила разработки на Blazor: жизненный цикл компонентов, привязка данных, управление состоянием и оптимизация рендеринга. CX Codex +1 798установок playwright-blazor-testing Aaronontheweb Скилл учит агента писать end-to-end тесты для Blazor Server и WebAssembly на Playwright: навигация, формы, авторизация, селекторы. CX Codex +1 537установок csharp-developer https://github.com/Jeffallan Настраивает правила разработки на .NET 8 и C# 12 для создания API, Blazor-приложений и работы с EF Core. Пригодится при проектировании сервисов с CQRS и Result pattern. CX Codex +1 4 800установок aspnet-core официальный openai Помогает проектировать, писать и обновлять приложения на ASP.NET Core по официальным гайдам Microsoft. Пригодится при работе с Blazor, Minimal API и gRPC. CX Codex +1 3 900установок c-sharp Mindrally Задаёт агенту стиль кода на C#: именование, async, DI, тесты, Blazor и Unity. Пригодится, когда проект на .NET и не хочется каждый раз объяснять конвенции. CX Codex +1 760установок fluentui-blazor официальный github Скилл подсказывает, как подключать Microsoft Fluent UI Blazor и собирать интерфейсы из Fluent-компонентов. Нужен при разработке и отладке Blazor-приложений. 9 000установок

Рядом в теме «Фронтенд и UI»

Всё по теме «Фронтенд и UI» →

Вопросы о coordinate-components

Как установить coordinate-components в Claude Code?

Выполните команду npx skills add dotnet/skills --skill coordinate-components -a claude-code в терминале проекта.

Что делает coordinate-components?

Скилл задаёт правила и шаблоны кода для синхронизации состояния между компонентами Blazor через каскадные значения, scoped-сервисы и DI.

Когда не стоит использовать этот скилл?

Инструкция не предназначена для простой передачи параметров родитель-потомок через [Parameter], сохранения состояния при пререндеринге и абстракций выборки данных.

Исходный текст

SKILL.md · MIT
---
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.

```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; }
```

**Rules:**
- Matched by **type**, not name. To cascade multiple values of the same type, add `Name`:
  ```razor
  <CascadingValue Value="primary" Name="PrimaryTheme">...</CascadingValue>
  ```
  ```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 `<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**.

```csharp
// 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:

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

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

```razor
@* 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:**

```csharp
public class CartState
{
    private readonly List<CartItem> _items = [];

    public IReadOnlyList<CartItem> 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:**

```csharp
builder.Services.AddScoped<CartState>();
```

**Subscribe in components:**

```razor
@inject CartState Cart
@implements IDisposable

<span class="badge">@Cart.Count</span>

@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`:

```csharp
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 `<CascadingValue>` 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<T>` 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<T>()`.

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 object** — `NotifyChangedAsync` 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 `<CascadingValue>` in a static layout expecting it to reach interactive children** — it won't cross render mode boundaries. Use DI-registered `CascadingValueSource<T>` 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<T>` 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`.

© dotnet · MIT · dotnet/skills

Смотрите также

Спонсоры проекта

Компании, которые помогают держать аналитику открытой