[{"data":1,"prerenderedAt":-1},["ShallowReactive",2],{"skill-dotnet-coordinate-components":3,"mdc-1oa53t-key":34,"related-repo-dotnet-coordinate-components":1844,"related-org-dotnet-coordinate-components":1951},{"slug":4,"name":4,"fn":5,"description":6,"org":7,"tags":11,"stars":22,"repoUrl":23,"updatedAt":24,"license":25,"forks":26,"topics":27,"repo":29,"sourceUrl":32,"mdContent":33},"coordinate-components","coordinate state between Blazor 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 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\u002FWebAssembly (see fetch-and-send-data).\n",{"slug":8,"name":9,"logoUrl":10,"githubOrg":8},"dotnet",".NET (Microsoft)","https:\u002F\u002Fpexgzepcugksgbtrxkhf.supabase.co\u002Fstorage\u002Fv1\u002Fobject\u002Fpublic\u002Forg-logos\u002Fdotnet.png",[12,16,19],{"name":13,"slug":14,"type":15},".NET","net","tag",{"name":17,"slug":18,"type":15},"Blazor","blazor",{"name":20,"slug":21,"type":15},"State Management","state-management",4576,"https:\u002F\u002Fgithub.com\u002Fdotnet\u002Fskills","2026-07-15T06:03:27.975634","MIT",332,[28],"agent-skills",{"repoUrl":23,"stars":22,"forks":26,"topics":30,"description":31},[28],"Repository for skills to assist AI coding agents with .NET and C#","https:\u002F\u002Fgithub.com\u002Fdotnet\u002Fskills\u002Ftree\u002FHEAD\u002Fplugins\u002Fdotnet-blazor\u002Fskills\u002Fcoordinate-components","---\nlicense: MIT\nname: coordinate-components\ndescription: >\n  Share state between components that don't have a direct parent-child parameter relationship,\n  using cascading values, scoped services with change events, or CascadingValueSource via DI.\n  USE WHEN the user needs a CascadingParameter or CascadingValue that works across render mode\n  boundaries, a shopping cart or notification count accessible from multiple pages, a theme or\n  user preference cascaded app-wide, or when components in different parts of the tree must\n  react when shared data changes. Also USE WHEN cascading values aren't reaching interactive\n  children in per-page interactivity mode, or when the user needs to understand scoped vs\n  singleton service lifetime for state on Blazor Server.\n  DO NOT USE for direct parent-child parameter passing or EventCallback (see author-component),\n  for persisting state across prerender-to-interactive transitions (see support-prerendering),\n  or for service abstractions for data fetching in Auto\u002FWebAssembly (see fetch-and-send-data).\n---\n\n# Coordinate Components\n\n## Step 1 — Read AGENTS.md\n\nRead `AGENTS.md` at the workspace root to learn the project's conventions before making changes.\n\n## Step 2 — Decide the scope\n\n| Need | Mechanism | When to use |\n|------|-----------|-------------|\n| Subtree (same render mode) | `CascadingValue` component | Theme, layout config within a layout |\n| App-wide (all render modes) | `CascadingValueSource\u003CT>` via DI | Current user, feature flags, theme shared globally |\n| Mutable shared state within a circuit | Scoped service + `Action` event | Shopping cart, notification count, selected filters |\n\nFor parent→child one level: use `[Parameter]` \u002F `EventCallback` (see `author-component` skill).\nFor persisting state across prerender→interactive: see `support-prerendering` skill.\n\n## Workflow (quick reference)\n\n1. Choose the mechanism from the table in Step 2\n2. If crossing render mode boundaries → use `CascadingValueSource\u003CT>` (Step 4)\n3. Register in `Program.cs` with `AddCascadingValue(...)` and `isFixed: false`\n4. Consume via `[CascadingParameter]` in child components\n5. Update via `NotifyChangedAsync(newValue)` — never page reload\n6. For additional mutable state within a circuit → add scoped service (Step 5)\n7. Wrap any `StateHasChanged` from background threads in `InvokeAsync`\n8. Implement `IDisposable` — dispose timers, cancel tokens, unsubscribe events\n\n## Step 3 — CascadingValue for subtree state\n\nWrap a subtree with `\u003CCascadingValue>` to flow data to all descendants without passing it through every intermediate component.\n\n```razor\n@* In a layout or parent component *@\n\u003CCascadingValue Value=\"theme\">\n    @Body\n\u003C\u002FCascadingValue>\n\n@code {\n    private ThemeInfo theme = new() { ButtonClass = \"btn-primary\" };\n}\n```\n\nConsume in any descendant:\n\n```csharp\n[CascadingParameter]\nprivate ThemeInfo? Theme { get; set; }\n```\n\n**Rules:**\n- Matched by **type**, not name. To cascade multiple values of the same type, add `Name`:\n  ```razor\n  \u003CCascadingValue Value=\"primary\" Name=\"PrimaryTheme\">...\u003C\u002FCascadingValue>\n  ```\n  ```csharp\n  [CascadingParameter(Name = \"PrimaryTheme\")]\n  private ThemeInfo? Primary { get; set; }\n  ```\n- Set `IsFixed=\"true\"` when the value never changes — avoids subscription overhead.\n- **Does NOT cross render mode boundaries.** A `\u003CCascadingValue>` in a static SSR parent is invisible to interactive children. See Step 6.\n\n## Step 4 — CascadingValueSource&lt;T&gt; for app-wide state\n\nRegister a `CascadingValueSource\u003CT>` in DI when the value must be available to **all components regardless of render mode**.\n\n```csharp\n\u002F\u002F Program.cs\nbuilder.Services.AddCascadingValue(sp =>\n{\n    var theme = new ThemeInfo { ButtonClass = \"btn-primary\" };\n    return new CascadingValueSource\u003CThemeInfo>(theme, isFixed: false);\n});\n```\n\nConsume identically to Step 3:\n\n```csharp\n[CascadingParameter]\nprivate ThemeInfo? Theme { get; set; }\n```\n\n**To update and notify subscribers**, either mutate the existing object or replace it:\n\n```razor\n@* Component that changes the theme *@\n@inject CascadingValueSource\u003CThemeInfo> ThemeSource\n\n\u003Cbutton @onclick=\"ToggleDarkMode\">Toggle theme\u003C\u002Fbutton>\n\n@code {\n    private bool isDark;\n\n    private async Task ToggleDarkMode()\n    {\n        isDark = !isDark;\n        \u002F\u002F Replace the value entirely:\n        var newTheme = new ThemeInfo { ButtonClass = isDark ? \"btn-dark\" : \"btn-primary\" };\n        await ThemeSource.NotifyChangedAsync(newTheme);\n    }\n}\n```\n\n`NotifyChangedAsync()` (no argument) also works — mutate the object and then call it. `NotifyChangedAsync(newValue)` replaces the value and notifies in one step.\n\n**Update protocol:** Whenever shared state changes, the component that changes it MUST inject `CascadingValueSource\u003CT>` 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.\n\n**Rules:**\n- `isFixed: false` enables change notifications. `isFixed: true` is better for truly static values (feature flags).\n- **Crosses render mode boundaries** — works for per-page interactivity, global interactivity, and WebAssembly. Key advantage over `\u003CCascadingValue>`.\n- 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.\n- For Auto\u002FWebAssembly apps, register in **both** server and `.Client` `Program.cs`. The type must be in a shared assembly.\n\n## Step 5 — Scoped state service with change events\n\nFor mutable shared state that multiple components read **and write** (shopping cart, notification count, filters), use a scoped service with an event for change notification.\n\n**Define the service:**\n\n```csharp\npublic class CartState\n{\n    private readonly List\u003CCartItem> _items = [];\n\n    public IReadOnlyList\u003CCartItem> Items => _items;\n    public int Count => _items.Count;\n\n    public event Action? OnChange;\n\n    public void Add(CartItem item)\n    {\n        _items.Add(item);\n        OnChange?.Invoke();\n    }\n\n    public void Remove(CartItem item)\n    {\n        _items.Remove(item);\n        OnChange?.Invoke();\n    }\n}\n```\n\n**Register as scoped:**\n\n```csharp\nbuilder.Services.AddScoped\u003CCartState>();\n```\n\n**Subscribe in components:**\n\n```razor\n@inject CartState Cart\n@implements IDisposable\n\n\u003Cspan class=\"badge\">@Cart.Count\u003C\u002Fspan>\n\n@code {\n    protected override void OnInitialized()\n    {\n        Cart.OnChange += StateHasChanged;\n    }\n\n    public void Dispose()\n    {\n        Cart.OnChange -= StateHasChanged;\n    }\n}\n```\n\nThe 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`:\n\n```csharp\nprivate Action? _handler;\n\nprotected override void OnInitialized()\n{\n    _handler = () => InvokeAsync(StateHasChanged);\n    Cart.OnChange += _handler;\n}\n\npublic void Dispose() => Cart.OnChange -= _handler;\n```\n\nStore the delegate in a field so you can unsubscribe the exact same instance.\n\n## Step 6 — Render mode and service lifetime rules\n\n### Cascading values don't cross render mode boundaries\n\nA `\u003CCascadingValue>` 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.\n\n**Fix:** Use `CascadingValueSource\u003CT>` 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.\n\n### Service lifetime on Server vs WebAssembly\n\n| Lifetime | Server | WebAssembly |\n|----------|--------|-------------|\n| **Scoped** | Per circuit (per user connection) | Per browser tab |\n| **Singleton** | Shared across ALL users | Per browser tab (safe) |\n| **Transient** | New instance per injection | New instance per injection |\n\nOn 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\u003CT>()`.\n\nOn WebAssembly, singletons are per-tab and safe. But code meant for **both** Server and WebAssembly (Auto mode) must use scoped.\n\n### Auto\u002FWebAssembly with prerendering\n\nState 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.\n\n## Don'ts\n\n- **Don't use a singleton for per-user state on Server** — all circuits share it, leaking state between users.\n- **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`).\n- **Don't forget to unsubscribe** — omitting `Dispose` on event subscriptions causes memory leaks that grow per-circuit.\n- **Don't use `\u003CCascadingValue>` in a static layout expecting it to reach interactive children** — it won't cross render mode boundaries. Use DI-registered `CascadingValueSource\u003CT>` or scoped services.\n- **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\u003CT>` and call `NotifyChangedAsync(newValue)` to push updates to all `[CascadingParameter]` subscribers without a page reload.\n- **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`.\n",{"data":35,"body":36},{"license":25,"name":4,"description":6},{"type":37,"children":38},"root",[39,47,54,69,75,182,219,225,337,343,356,442,447,472,481,572,578,597,652,657,677,687,825,843,882,889,973,979,991,999,1168,1176,1190,1198,1324,1359,1434,1439,1445,1452,1487,1504,1510,1599,1618,1629,1635,1669,1675,1838],{"type":40,"tag":41,"props":42,"children":43},"element","h1",{"id":4},[44],{"type":45,"value":46},"text","Coordinate Components",{"type":40,"tag":48,"props":49,"children":51},"h2",{"id":50},"step-1-read-agentsmd",[52],{"type":45,"value":53},"Step 1 — Read AGENTS.md",{"type":40,"tag":55,"props":56,"children":57},"p",{},[58,60,67],{"type":45,"value":59},"Read ",{"type":40,"tag":61,"props":62,"children":64},"code",{"className":63},[],[65],{"type":45,"value":66},"AGENTS.md",{"type":45,"value":68}," at the workspace root to learn the project's conventions before making changes.",{"type":40,"tag":48,"props":70,"children":72},{"id":71},"step-2-decide-the-scope",[73],{"type":45,"value":74},"Step 2 — Decide the scope",{"type":40,"tag":76,"props":77,"children":78},"table",{},[79,103],{"type":40,"tag":80,"props":81,"children":82},"thead",{},[83],{"type":40,"tag":84,"props":85,"children":86},"tr",{},[87,93,98],{"type":40,"tag":88,"props":89,"children":90},"th",{},[91],{"type":45,"value":92},"Need",{"type":40,"tag":88,"props":94,"children":95},{},[96],{"type":45,"value":97},"Mechanism",{"type":40,"tag":88,"props":99,"children":100},{},[101],{"type":45,"value":102},"When to use",{"type":40,"tag":104,"props":105,"children":106},"tbody",{},[107,132,156],{"type":40,"tag":84,"props":108,"children":109},{},[110,116,127],{"type":40,"tag":111,"props":112,"children":113},"td",{},[114],{"type":45,"value":115},"Subtree (same render mode)",{"type":40,"tag":111,"props":117,"children":118},{},[119,125],{"type":40,"tag":61,"props":120,"children":122},{"className":121},[],[123],{"type":45,"value":124},"CascadingValue",{"type":45,"value":126}," component",{"type":40,"tag":111,"props":128,"children":129},{},[130],{"type":45,"value":131},"Theme, layout config within a layout",{"type":40,"tag":84,"props":133,"children":134},{},[135,140,151],{"type":40,"tag":111,"props":136,"children":137},{},[138],{"type":45,"value":139},"App-wide (all render modes)",{"type":40,"tag":111,"props":141,"children":142},{},[143,149],{"type":40,"tag":61,"props":144,"children":146},{"className":145},[],[147],{"type":45,"value":148},"CascadingValueSource\u003CT>",{"type":45,"value":150}," via DI",{"type":40,"tag":111,"props":152,"children":153},{},[154],{"type":45,"value":155},"Current user, feature flags, theme shared globally",{"type":40,"tag":84,"props":157,"children":158},{},[159,164,177],{"type":40,"tag":111,"props":160,"children":161},{},[162],{"type":45,"value":163},"Mutable shared state within a circuit",{"type":40,"tag":111,"props":165,"children":166},{},[167,169,175],{"type":45,"value":168},"Scoped service + ",{"type":40,"tag":61,"props":170,"children":172},{"className":171},[],[173],{"type":45,"value":174},"Action",{"type":45,"value":176}," event",{"type":40,"tag":111,"props":178,"children":179},{},[180],{"type":45,"value":181},"Shopping cart, notification count, selected filters",{"type":40,"tag":55,"props":183,"children":184},{},[185,187,193,195,201,203,209,211,217],{"type":45,"value":186},"For parent→child one level: use ",{"type":40,"tag":61,"props":188,"children":190},{"className":189},[],[191],{"type":45,"value":192},"[Parameter]",{"type":45,"value":194}," \u002F ",{"type":40,"tag":61,"props":196,"children":198},{"className":197},[],[199],{"type":45,"value":200},"EventCallback",{"type":45,"value":202}," (see ",{"type":40,"tag":61,"props":204,"children":206},{"className":205},[],[207],{"type":45,"value":208},"author-component",{"type":45,"value":210}," skill).\nFor persisting state across prerender→interactive: see ",{"type":40,"tag":61,"props":212,"children":214},{"className":213},[],[215],{"type":45,"value":216},"support-prerendering",{"type":45,"value":218}," skill.",{"type":40,"tag":48,"props":220,"children":222},{"id":221},"workflow-quick-reference",[223],{"type":45,"value":224},"Workflow (quick reference)",{"type":40,"tag":226,"props":227,"children":228},"ol",{},[229,235,247,274,287,300,305,324],{"type":40,"tag":230,"props":231,"children":232},"li",{},[233],{"type":45,"value":234},"Choose the mechanism from the table in Step 2",{"type":40,"tag":230,"props":236,"children":237},{},[238,240,245],{"type":45,"value":239},"If crossing render mode boundaries → use ",{"type":40,"tag":61,"props":241,"children":243},{"className":242},[],[244],{"type":45,"value":148},{"type":45,"value":246}," (Step 4)",{"type":40,"tag":230,"props":248,"children":249},{},[250,252,258,260,266,268],{"type":45,"value":251},"Register in ",{"type":40,"tag":61,"props":253,"children":255},{"className":254},[],[256],{"type":45,"value":257},"Program.cs",{"type":45,"value":259}," with ",{"type":40,"tag":61,"props":261,"children":263},{"className":262},[],[264],{"type":45,"value":265},"AddCascadingValue(...)",{"type":45,"value":267}," and ",{"type":40,"tag":61,"props":269,"children":271},{"className":270},[],[272],{"type":45,"value":273},"isFixed: false",{"type":40,"tag":230,"props":275,"children":276},{},[277,279,285],{"type":45,"value":278},"Consume via ",{"type":40,"tag":61,"props":280,"children":282},{"className":281},[],[283],{"type":45,"value":284},"[CascadingParameter]",{"type":45,"value":286}," in child components",{"type":40,"tag":230,"props":288,"children":289},{},[290,292,298],{"type":45,"value":291},"Update via ",{"type":40,"tag":61,"props":293,"children":295},{"className":294},[],[296],{"type":45,"value":297},"NotifyChangedAsync(newValue)",{"type":45,"value":299}," — never page reload",{"type":40,"tag":230,"props":301,"children":302},{},[303],{"type":45,"value":304},"For additional mutable state within a circuit → add scoped service (Step 5)",{"type":40,"tag":230,"props":306,"children":307},{},[308,310,316,318],{"type":45,"value":309},"Wrap any ",{"type":40,"tag":61,"props":311,"children":313},{"className":312},[],[314],{"type":45,"value":315},"StateHasChanged",{"type":45,"value":317}," from background threads in ",{"type":40,"tag":61,"props":319,"children":321},{"className":320},[],[322],{"type":45,"value":323},"InvokeAsync",{"type":40,"tag":230,"props":325,"children":326},{},[327,329,335],{"type":45,"value":328},"Implement ",{"type":40,"tag":61,"props":330,"children":332},{"className":331},[],[333],{"type":45,"value":334},"IDisposable",{"type":45,"value":336}," — dispose timers, cancel tokens, unsubscribe events",{"type":40,"tag":48,"props":338,"children":340},{"id":339},"step-3-cascadingvalue-for-subtree-state",[341],{"type":45,"value":342},"Step 3 — CascadingValue for subtree state",{"type":40,"tag":55,"props":344,"children":345},{},[346,348,354],{"type":45,"value":347},"Wrap a subtree with ",{"type":40,"tag":61,"props":349,"children":351},{"className":350},[],[352],{"type":45,"value":353},"\u003CCascadingValue>",{"type":45,"value":355}," to flow data to all descendants without passing it through every intermediate component.",{"type":40,"tag":357,"props":358,"children":363},"pre",{"className":359,"code":360,"language":361,"meta":362,"style":362},"language-razor shiki shiki-themes material-theme-lighter material-theme material-theme-palenight","@* In a layout or parent component *@\n\u003CCascadingValue Value=\"theme\">\n    @Body\n\u003C\u002FCascadingValue>\n\n@code {\n    private ThemeInfo theme = new() { ButtonClass = \"btn-primary\" };\n}\n","razor","",[364],{"type":40,"tag":61,"props":365,"children":366},{"__ignoreMap":362},[367,378,387,396,405,415,424,433],{"type":40,"tag":368,"props":369,"children":372},"span",{"class":370,"line":371},"line",1,[373],{"type":40,"tag":368,"props":374,"children":375},{},[376],{"type":45,"value":377},"@* In a layout or parent component *@\n",{"type":40,"tag":368,"props":379,"children":381},{"class":370,"line":380},2,[382],{"type":40,"tag":368,"props":383,"children":384},{},[385],{"type":45,"value":386},"\u003CCascadingValue Value=\"theme\">\n",{"type":40,"tag":368,"props":388,"children":390},{"class":370,"line":389},3,[391],{"type":40,"tag":368,"props":392,"children":393},{},[394],{"type":45,"value":395},"    @Body\n",{"type":40,"tag":368,"props":397,"children":399},{"class":370,"line":398},4,[400],{"type":40,"tag":368,"props":401,"children":402},{},[403],{"type":45,"value":404},"\u003C\u002FCascadingValue>\n",{"type":40,"tag":368,"props":406,"children":408},{"class":370,"line":407},5,[409],{"type":40,"tag":368,"props":410,"children":412},{"emptyLinePlaceholder":411},true,[413],{"type":45,"value":414},"\n",{"type":40,"tag":368,"props":416,"children":418},{"class":370,"line":417},6,[419],{"type":40,"tag":368,"props":420,"children":421},{},[422],{"type":45,"value":423},"@code {\n",{"type":40,"tag":368,"props":425,"children":427},{"class":370,"line":426},7,[428],{"type":40,"tag":368,"props":429,"children":430},{},[431],{"type":45,"value":432},"    private ThemeInfo theme = new() { ButtonClass = \"btn-primary\" };\n",{"type":40,"tag":368,"props":434,"children":436},{"class":370,"line":435},8,[437],{"type":40,"tag":368,"props":438,"children":439},{},[440],{"type":45,"value":441},"}\n",{"type":40,"tag":55,"props":443,"children":444},{},[445],{"type":45,"value":446},"Consume in any descendant:",{"type":40,"tag":357,"props":448,"children":452},{"className":449,"code":450,"language":451,"meta":362,"style":362},"language-csharp shiki shiki-themes material-theme-lighter material-theme material-theme-palenight","[CascadingParameter]\nprivate ThemeInfo? Theme { get; set; }\n","csharp",[453],{"type":40,"tag":61,"props":454,"children":455},{"__ignoreMap":362},[456,464],{"type":40,"tag":368,"props":457,"children":458},{"class":370,"line":371},[459],{"type":40,"tag":368,"props":460,"children":461},{},[462],{"type":45,"value":463},"[CascadingParameter]\n",{"type":40,"tag":368,"props":465,"children":466},{"class":370,"line":380},[467],{"type":40,"tag":368,"props":468,"children":469},{},[470],{"type":45,"value":471},"private ThemeInfo? Theme { get; set; }\n",{"type":40,"tag":55,"props":473,"children":474},{},[475],{"type":40,"tag":476,"props":477,"children":478},"strong",{},[479],{"type":45,"value":480},"Rules:",{"type":40,"tag":482,"props":483,"children":484},"ul",{},[485,542,555],{"type":40,"tag":230,"props":486,"children":487},{},[488,490,495,497,503,505,519],{"type":45,"value":489},"Matched by ",{"type":40,"tag":476,"props":491,"children":492},{},[493],{"type":45,"value":494},"type",{"type":45,"value":496},", not name. To cascade multiple values of the same type, add ",{"type":40,"tag":61,"props":498,"children":500},{"className":499},[],[501],{"type":45,"value":502},"Name",{"type":45,"value":504},":\n",{"type":40,"tag":357,"props":506,"children":508},{"className":359,"code":507,"language":361,"meta":362,"style":362},"\u003CCascadingValue Value=\"primary\" Name=\"PrimaryTheme\">...\u003C\u002FCascadingValue>\n",[509],{"type":40,"tag":61,"props":510,"children":511},{"__ignoreMap":362},[512],{"type":40,"tag":368,"props":513,"children":514},{"class":370,"line":371},[515],{"type":40,"tag":368,"props":516,"children":517},{},[518],{"type":45,"value":507},{"type":40,"tag":357,"props":520,"children":522},{"className":449,"code":521,"language":451,"meta":362,"style":362},"[CascadingParameter(Name = \"PrimaryTheme\")]\nprivate ThemeInfo? Primary { get; set; }\n",[523],{"type":40,"tag":61,"props":524,"children":525},{"__ignoreMap":362},[526,534],{"type":40,"tag":368,"props":527,"children":528},{"class":370,"line":371},[529],{"type":40,"tag":368,"props":530,"children":531},{},[532],{"type":45,"value":533},"[CascadingParameter(Name = \"PrimaryTheme\")]\n",{"type":40,"tag":368,"props":535,"children":536},{"class":370,"line":380},[537],{"type":40,"tag":368,"props":538,"children":539},{},[540],{"type":45,"value":541},"private ThemeInfo? Primary { get; set; }\n",{"type":40,"tag":230,"props":543,"children":544},{},[545,547,553],{"type":45,"value":546},"Set ",{"type":40,"tag":61,"props":548,"children":550},{"className":549},[],[551],{"type":45,"value":552},"IsFixed=\"true\"",{"type":45,"value":554}," when the value never changes — avoids subscription overhead.",{"type":40,"tag":230,"props":556,"children":557},{},[558,563,565,570],{"type":40,"tag":476,"props":559,"children":560},{},[561],{"type":45,"value":562},"Does NOT cross render mode boundaries.",{"type":45,"value":564}," A ",{"type":40,"tag":61,"props":566,"children":568},{"className":567},[],[569],{"type":45,"value":353},{"type":45,"value":571}," in a static SSR parent is invisible to interactive children. See Step 6.",{"type":40,"tag":48,"props":573,"children":575},{"id":574},"step-4-cascadingvaluesourcet-for-app-wide-state",[576],{"type":45,"value":577},"Step 4 — CascadingValueSource\u003CT> for app-wide state",{"type":40,"tag":55,"props":579,"children":580},{},[581,583,588,590,595],{"type":45,"value":582},"Register a ",{"type":40,"tag":61,"props":584,"children":586},{"className":585},[],[587],{"type":45,"value":148},{"type":45,"value":589}," in DI when the value must be available to ",{"type":40,"tag":476,"props":591,"children":592},{},[593],{"type":45,"value":594},"all components regardless of render mode",{"type":45,"value":596},".",{"type":40,"tag":357,"props":598,"children":600},{"className":449,"code":599,"language":451,"meta":362,"style":362},"\u002F\u002F Program.cs\nbuilder.Services.AddCascadingValue(sp =>\n{\n    var theme = new ThemeInfo { ButtonClass = \"btn-primary\" };\n    return new CascadingValueSource\u003CThemeInfo>(theme, isFixed: false);\n});\n",[601],{"type":40,"tag":61,"props":602,"children":603},{"__ignoreMap":362},[604,612,620,628,636,644],{"type":40,"tag":368,"props":605,"children":606},{"class":370,"line":371},[607],{"type":40,"tag":368,"props":608,"children":609},{},[610],{"type":45,"value":611},"\u002F\u002F Program.cs\n",{"type":40,"tag":368,"props":613,"children":614},{"class":370,"line":380},[615],{"type":40,"tag":368,"props":616,"children":617},{},[618],{"type":45,"value":619},"builder.Services.AddCascadingValue(sp =>\n",{"type":40,"tag":368,"props":621,"children":622},{"class":370,"line":389},[623],{"type":40,"tag":368,"props":624,"children":625},{},[626],{"type":45,"value":627},"{\n",{"type":40,"tag":368,"props":629,"children":630},{"class":370,"line":398},[631],{"type":40,"tag":368,"props":632,"children":633},{},[634],{"type":45,"value":635},"    var theme = new ThemeInfo { ButtonClass = \"btn-primary\" };\n",{"type":40,"tag":368,"props":637,"children":638},{"class":370,"line":407},[639],{"type":40,"tag":368,"props":640,"children":641},{},[642],{"type":45,"value":643},"    return new CascadingValueSource\u003CThemeInfo>(theme, isFixed: false);\n",{"type":40,"tag":368,"props":645,"children":646},{"class":370,"line":417},[647],{"type":40,"tag":368,"props":648,"children":649},{},[650],{"type":45,"value":651},"});\n",{"type":40,"tag":55,"props":653,"children":654},{},[655],{"type":45,"value":656},"Consume identically to Step 3:",{"type":40,"tag":357,"props":658,"children":659},{"className":449,"code":450,"language":451,"meta":362,"style":362},[660],{"type":40,"tag":61,"props":661,"children":662},{"__ignoreMap":362},[663,670],{"type":40,"tag":368,"props":664,"children":665},{"class":370,"line":371},[666],{"type":40,"tag":368,"props":667,"children":668},{},[669],{"type":45,"value":463},{"type":40,"tag":368,"props":671,"children":672},{"class":370,"line":380},[673],{"type":40,"tag":368,"props":674,"children":675},{},[676],{"type":45,"value":471},{"type":40,"tag":55,"props":678,"children":679},{},[680,685],{"type":40,"tag":476,"props":681,"children":682},{},[683],{"type":45,"value":684},"To update and notify subscribers",{"type":45,"value":686},", either mutate the existing object or replace it:",{"type":40,"tag":357,"props":688,"children":690},{"className":359,"code":689,"language":361,"meta":362,"style":362},"@* Component that changes the theme *@\n@inject CascadingValueSource\u003CThemeInfo> ThemeSource\n\n\u003Cbutton @onclick=\"ToggleDarkMode\">Toggle theme\u003C\u002Fbutton>\n\n@code {\n    private bool isDark;\n\n    private async Task ToggleDarkMode()\n    {\n        isDark = !isDark;\n        \u002F\u002F Replace the value entirely:\n        var newTheme = new ThemeInfo { ButtonClass = isDark ? \"btn-dark\" : \"btn-primary\" };\n        await ThemeSource.NotifyChangedAsync(newTheme);\n    }\n}\n",[691],{"type":40,"tag":61,"props":692,"children":693},{"__ignoreMap":362},[694,702,710,717,725,732,739,747,754,763,772,781,790,799,808,817],{"type":40,"tag":368,"props":695,"children":696},{"class":370,"line":371},[697],{"type":40,"tag":368,"props":698,"children":699},{},[700],{"type":45,"value":701},"@* Component that changes the theme *@\n",{"type":40,"tag":368,"props":703,"children":704},{"class":370,"line":380},[705],{"type":40,"tag":368,"props":706,"children":707},{},[708],{"type":45,"value":709},"@inject CascadingValueSource\u003CThemeInfo> ThemeSource\n",{"type":40,"tag":368,"props":711,"children":712},{"class":370,"line":389},[713],{"type":40,"tag":368,"props":714,"children":715},{"emptyLinePlaceholder":411},[716],{"type":45,"value":414},{"type":40,"tag":368,"props":718,"children":719},{"class":370,"line":398},[720],{"type":40,"tag":368,"props":721,"children":722},{},[723],{"type":45,"value":724},"\u003Cbutton @onclick=\"ToggleDarkMode\">Toggle theme\u003C\u002Fbutton>\n",{"type":40,"tag":368,"props":726,"children":727},{"class":370,"line":407},[728],{"type":40,"tag":368,"props":729,"children":730},{"emptyLinePlaceholder":411},[731],{"type":45,"value":414},{"type":40,"tag":368,"props":733,"children":734},{"class":370,"line":417},[735],{"type":40,"tag":368,"props":736,"children":737},{},[738],{"type":45,"value":423},{"type":40,"tag":368,"props":740,"children":741},{"class":370,"line":426},[742],{"type":40,"tag":368,"props":743,"children":744},{},[745],{"type":45,"value":746},"    private bool isDark;\n",{"type":40,"tag":368,"props":748,"children":749},{"class":370,"line":435},[750],{"type":40,"tag":368,"props":751,"children":752},{"emptyLinePlaceholder":411},[753],{"type":45,"value":414},{"type":40,"tag":368,"props":755,"children":757},{"class":370,"line":756},9,[758],{"type":40,"tag":368,"props":759,"children":760},{},[761],{"type":45,"value":762},"    private async Task ToggleDarkMode()\n",{"type":40,"tag":368,"props":764,"children":766},{"class":370,"line":765},10,[767],{"type":40,"tag":368,"props":768,"children":769},{},[770],{"type":45,"value":771},"    {\n",{"type":40,"tag":368,"props":773,"children":775},{"class":370,"line":774},11,[776],{"type":40,"tag":368,"props":777,"children":778},{},[779],{"type":45,"value":780},"        isDark = !isDark;\n",{"type":40,"tag":368,"props":782,"children":784},{"class":370,"line":783},12,[785],{"type":40,"tag":368,"props":786,"children":787},{},[788],{"type":45,"value":789},"        \u002F\u002F Replace the value entirely:\n",{"type":40,"tag":368,"props":791,"children":793},{"class":370,"line":792},13,[794],{"type":40,"tag":368,"props":795,"children":796},{},[797],{"type":45,"value":798},"        var newTheme = new ThemeInfo { ButtonClass = isDark ? \"btn-dark\" : \"btn-primary\" };\n",{"type":40,"tag":368,"props":800,"children":802},{"class":370,"line":801},14,[803],{"type":40,"tag":368,"props":804,"children":805},{},[806],{"type":45,"value":807},"        await ThemeSource.NotifyChangedAsync(newTheme);\n",{"type":40,"tag":368,"props":809,"children":811},{"class":370,"line":810},15,[812],{"type":40,"tag":368,"props":813,"children":814},{},[815],{"type":45,"value":816},"    }\n",{"type":40,"tag":368,"props":818,"children":820},{"class":370,"line":819},16,[821],{"type":40,"tag":368,"props":822,"children":823},{},[824],{"type":45,"value":441},{"type":40,"tag":55,"props":826,"children":827},{},[828,834,836,841],{"type":40,"tag":61,"props":829,"children":831},{"className":830},[],[832],{"type":45,"value":833},"NotifyChangedAsync()",{"type":45,"value":835}," (no argument) also works — mutate the object and then call it. ",{"type":40,"tag":61,"props":837,"children":839},{"className":838},[],[840],{"type":45,"value":297},{"type":45,"value":842}," replaces the value and notifies in one step.",{"type":40,"tag":55,"props":844,"children":845},{},[846,851,853,858,860,865,867,872,874,880],{"type":40,"tag":476,"props":847,"children":848},{},[849],{"type":45,"value":850},"Update protocol:",{"type":45,"value":852}," Whenever shared state changes, the component that changes it MUST inject ",{"type":40,"tag":61,"props":854,"children":856},{"className":855},[],[857],{"type":45,"value":148},{"type":45,"value":859}," and call ",{"type":40,"tag":61,"props":861,"children":863},{"className":862},[],[864],{"type":45,"value":833},{"type":45,"value":866},". This is the only mechanism that triggers re-rendering in all ",{"type":40,"tag":61,"props":868,"children":870},{"className":869},[],[871],{"type":45,"value":284},{"type":45,"value":873}," subscribers. Without this call, no subscribers update. Do not use ",{"type":40,"tag":61,"props":875,"children":877},{"className":876},[],[878],{"type":45,"value":879},"NavigationManager.Refresh()",{"type":45,"value":881}," or page reloads as a substitute.",{"type":40,"tag":55,"props":883,"children":884},{},[885],{"type":40,"tag":476,"props":886,"children":887},{},[888],{"type":45,"value":480},{"type":40,"tag":482,"props":890,"children":891},{},[892,910,926,946],{"type":40,"tag":230,"props":893,"children":894},{},[895,900,902,908],{"type":40,"tag":61,"props":896,"children":898},{"className":897},[],[899],{"type":45,"value":273},{"type":45,"value":901}," enables change notifications. ",{"type":40,"tag":61,"props":903,"children":905},{"className":904},[],[906],{"type":45,"value":907},"isFixed: true",{"type":45,"value":909}," is better for truly static values (feature flags).",{"type":40,"tag":230,"props":911,"children":912},{},[913,918,920,925],{"type":40,"tag":476,"props":914,"children":915},{},[916],{"type":45,"value":917},"Crosses render mode boundaries",{"type":45,"value":919}," — works for per-page interactivity, global interactivity, and WebAssembly. Key advantage over ",{"type":40,"tag":61,"props":921,"children":923},{"className":922},[],[924],{"type":45,"value":353},{"type":45,"value":596},{"type":40,"tag":230,"props":927,"children":928},{},[929,931,936,938,944],{"type":45,"value":930},"Keep cascaded types ",{"type":40,"tag":476,"props":932,"children":933},{},[934],{"type":45,"value":935},"granular",{"type":45,"value":937},". Every ",{"type":40,"tag":61,"props":939,"children":941},{"className":940},[],[942],{"type":45,"value":943},"NotifyChangedAsync",{"type":45,"value":945}," re-renders ALL subscribers regardless of which property changed. Don't put all app state into one cascaded type.",{"type":40,"tag":230,"props":947,"children":948},{},[949,951,956,958,964,966,971],{"type":45,"value":950},"For Auto\u002FWebAssembly apps, register in ",{"type":40,"tag":476,"props":952,"children":953},{},[954],{"type":45,"value":955},"both",{"type":45,"value":957}," server and ",{"type":40,"tag":61,"props":959,"children":961},{"className":960},[],[962],{"type":45,"value":963},".Client",{"type":45,"value":965}," ",{"type":40,"tag":61,"props":967,"children":969},{"className":968},[],[970],{"type":45,"value":257},{"type":45,"value":972},". The type must be in a shared assembly.",{"type":40,"tag":48,"props":974,"children":976},{"id":975},"step-5-scoped-state-service-with-change-events",[977],{"type":45,"value":978},"Step 5 — Scoped state service with change events",{"type":40,"tag":55,"props":980,"children":981},{},[982,984,989],{"type":45,"value":983},"For mutable shared state that multiple components read ",{"type":40,"tag":476,"props":985,"children":986},{},[987],{"type":45,"value":988},"and write",{"type":45,"value":990}," (shopping cart, notification count, filters), use a scoped service with an event for change notification.",{"type":40,"tag":55,"props":992,"children":993},{},[994],{"type":40,"tag":476,"props":995,"children":996},{},[997],{"type":45,"value":998},"Define the service:",{"type":40,"tag":357,"props":1000,"children":1002},{"className":449,"code":1001,"language":451,"meta":362,"style":362},"public class CartState\n{\n    private readonly List\u003CCartItem> _items = [];\n\n    public IReadOnlyList\u003CCartItem> Items => _items;\n    public int Count => _items.Count;\n\n    public event Action? OnChange;\n\n    public void Add(CartItem item)\n    {\n        _items.Add(item);\n        OnChange?.Invoke();\n    }\n\n    public void Remove(CartItem item)\n    {\n        _items.Remove(item);\n        OnChange?.Invoke();\n    }\n}\n",[1003],{"type":40,"tag":61,"props":1004,"children":1005},{"__ignoreMap":362},[1006,1014,1021,1029,1036,1044,1052,1059,1067,1074,1082,1089,1097,1105,1112,1119,1127,1135,1144,1152,1160],{"type":40,"tag":368,"props":1007,"children":1008},{"class":370,"line":371},[1009],{"type":40,"tag":368,"props":1010,"children":1011},{},[1012],{"type":45,"value":1013},"public class CartState\n",{"type":40,"tag":368,"props":1015,"children":1016},{"class":370,"line":380},[1017],{"type":40,"tag":368,"props":1018,"children":1019},{},[1020],{"type":45,"value":627},{"type":40,"tag":368,"props":1022,"children":1023},{"class":370,"line":389},[1024],{"type":40,"tag":368,"props":1025,"children":1026},{},[1027],{"type":45,"value":1028},"    private readonly List\u003CCartItem> _items = [];\n",{"type":40,"tag":368,"props":1030,"children":1031},{"class":370,"line":398},[1032],{"type":40,"tag":368,"props":1033,"children":1034},{"emptyLinePlaceholder":411},[1035],{"type":45,"value":414},{"type":40,"tag":368,"props":1037,"children":1038},{"class":370,"line":407},[1039],{"type":40,"tag":368,"props":1040,"children":1041},{},[1042],{"type":45,"value":1043},"    public IReadOnlyList\u003CCartItem> Items => _items;\n",{"type":40,"tag":368,"props":1045,"children":1046},{"class":370,"line":417},[1047],{"type":40,"tag":368,"props":1048,"children":1049},{},[1050],{"type":45,"value":1051},"    public int Count => _items.Count;\n",{"type":40,"tag":368,"props":1053,"children":1054},{"class":370,"line":426},[1055],{"type":40,"tag":368,"props":1056,"children":1057},{"emptyLinePlaceholder":411},[1058],{"type":45,"value":414},{"type":40,"tag":368,"props":1060,"children":1061},{"class":370,"line":435},[1062],{"type":40,"tag":368,"props":1063,"children":1064},{},[1065],{"type":45,"value":1066},"    public event Action? OnChange;\n",{"type":40,"tag":368,"props":1068,"children":1069},{"class":370,"line":756},[1070],{"type":40,"tag":368,"props":1071,"children":1072},{"emptyLinePlaceholder":411},[1073],{"type":45,"value":414},{"type":40,"tag":368,"props":1075,"children":1076},{"class":370,"line":765},[1077],{"type":40,"tag":368,"props":1078,"children":1079},{},[1080],{"type":45,"value":1081},"    public void Add(CartItem item)\n",{"type":40,"tag":368,"props":1083,"children":1084},{"class":370,"line":774},[1085],{"type":40,"tag":368,"props":1086,"children":1087},{},[1088],{"type":45,"value":771},{"type":40,"tag":368,"props":1090,"children":1091},{"class":370,"line":783},[1092],{"type":40,"tag":368,"props":1093,"children":1094},{},[1095],{"type":45,"value":1096},"        _items.Add(item);\n",{"type":40,"tag":368,"props":1098,"children":1099},{"class":370,"line":792},[1100],{"type":40,"tag":368,"props":1101,"children":1102},{},[1103],{"type":45,"value":1104},"        OnChange?.Invoke();\n",{"type":40,"tag":368,"props":1106,"children":1107},{"class":370,"line":801},[1108],{"type":40,"tag":368,"props":1109,"children":1110},{},[1111],{"type":45,"value":816},{"type":40,"tag":368,"props":1113,"children":1114},{"class":370,"line":810},[1115],{"type":40,"tag":368,"props":1116,"children":1117},{"emptyLinePlaceholder":411},[1118],{"type":45,"value":414},{"type":40,"tag":368,"props":1120,"children":1121},{"class":370,"line":819},[1122],{"type":40,"tag":368,"props":1123,"children":1124},{},[1125],{"type":45,"value":1126},"    public void Remove(CartItem item)\n",{"type":40,"tag":368,"props":1128,"children":1130},{"class":370,"line":1129},17,[1131],{"type":40,"tag":368,"props":1132,"children":1133},{},[1134],{"type":45,"value":771},{"type":40,"tag":368,"props":1136,"children":1138},{"class":370,"line":1137},18,[1139],{"type":40,"tag":368,"props":1140,"children":1141},{},[1142],{"type":45,"value":1143},"        _items.Remove(item);\n",{"type":40,"tag":368,"props":1145,"children":1147},{"class":370,"line":1146},19,[1148],{"type":40,"tag":368,"props":1149,"children":1150},{},[1151],{"type":45,"value":1104},{"type":40,"tag":368,"props":1153,"children":1155},{"class":370,"line":1154},20,[1156],{"type":40,"tag":368,"props":1157,"children":1158},{},[1159],{"type":45,"value":816},{"type":40,"tag":368,"props":1161,"children":1163},{"class":370,"line":1162},21,[1164],{"type":40,"tag":368,"props":1165,"children":1166},{},[1167],{"type":45,"value":441},{"type":40,"tag":55,"props":1169,"children":1170},{},[1171],{"type":40,"tag":476,"props":1172,"children":1173},{},[1174],{"type":45,"value":1175},"Register as scoped:",{"type":40,"tag":357,"props":1177,"children":1179},{"className":449,"code":1178,"language":451,"meta":362,"style":362},"builder.Services.AddScoped\u003CCartState>();\n",[1180],{"type":40,"tag":61,"props":1181,"children":1182},{"__ignoreMap":362},[1183],{"type":40,"tag":368,"props":1184,"children":1185},{"class":370,"line":371},[1186],{"type":40,"tag":368,"props":1187,"children":1188},{},[1189],{"type":45,"value":1178},{"type":40,"tag":55,"props":1191,"children":1192},{},[1193],{"type":40,"tag":476,"props":1194,"children":1195},{},[1196],{"type":45,"value":1197},"Subscribe in components:",{"type":40,"tag":357,"props":1199,"children":1201},{"className":359,"code":1200,"language":361,"meta":362,"style":362},"@inject CartState Cart\n@implements IDisposable\n\n\u003Cspan class=\"badge\">@Cart.Count\u003C\u002Fspan>\n\n@code {\n    protected override void OnInitialized()\n    {\n        Cart.OnChange += StateHasChanged;\n    }\n\n    public void Dispose()\n    {\n        Cart.OnChange -= StateHasChanged;\n    }\n}\n",[1202],{"type":40,"tag":61,"props":1203,"children":1204},{"__ignoreMap":362},[1205,1213,1221,1228,1236,1243,1250,1258,1265,1273,1280,1287,1295,1302,1310,1317],{"type":40,"tag":368,"props":1206,"children":1207},{"class":370,"line":371},[1208],{"type":40,"tag":368,"props":1209,"children":1210},{},[1211],{"type":45,"value":1212},"@inject CartState Cart\n",{"type":40,"tag":368,"props":1214,"children":1215},{"class":370,"line":380},[1216],{"type":40,"tag":368,"props":1217,"children":1218},{},[1219],{"type":45,"value":1220},"@implements IDisposable\n",{"type":40,"tag":368,"props":1222,"children":1223},{"class":370,"line":389},[1224],{"type":40,"tag":368,"props":1225,"children":1226},{"emptyLinePlaceholder":411},[1227],{"type":45,"value":414},{"type":40,"tag":368,"props":1229,"children":1230},{"class":370,"line":398},[1231],{"type":40,"tag":368,"props":1232,"children":1233},{},[1234],{"type":45,"value":1235},"\u003Cspan class=\"badge\">@Cart.Count\u003C\u002Fspan>\n",{"type":40,"tag":368,"props":1237,"children":1238},{"class":370,"line":407},[1239],{"type":40,"tag":368,"props":1240,"children":1241},{"emptyLinePlaceholder":411},[1242],{"type":45,"value":414},{"type":40,"tag":368,"props":1244,"children":1245},{"class":370,"line":417},[1246],{"type":40,"tag":368,"props":1247,"children":1248},{},[1249],{"type":45,"value":423},{"type":40,"tag":368,"props":1251,"children":1252},{"class":370,"line":426},[1253],{"type":40,"tag":368,"props":1254,"children":1255},{},[1256],{"type":45,"value":1257},"    protected override void OnInitialized()\n",{"type":40,"tag":368,"props":1259,"children":1260},{"class":370,"line":435},[1261],{"type":40,"tag":368,"props":1262,"children":1263},{},[1264],{"type":45,"value":771},{"type":40,"tag":368,"props":1266,"children":1267},{"class":370,"line":756},[1268],{"type":40,"tag":368,"props":1269,"children":1270},{},[1271],{"type":45,"value":1272},"        Cart.OnChange += StateHasChanged;\n",{"type":40,"tag":368,"props":1274,"children":1275},{"class":370,"line":765},[1276],{"type":40,"tag":368,"props":1277,"children":1278},{},[1279],{"type":45,"value":816},{"type":40,"tag":368,"props":1281,"children":1282},{"class":370,"line":774},[1283],{"type":40,"tag":368,"props":1284,"children":1285},{"emptyLinePlaceholder":411},[1286],{"type":45,"value":414},{"type":40,"tag":368,"props":1288,"children":1289},{"class":370,"line":783},[1290],{"type":40,"tag":368,"props":1291,"children":1292},{},[1293],{"type":45,"value":1294},"    public void Dispose()\n",{"type":40,"tag":368,"props":1296,"children":1297},{"class":370,"line":792},[1298],{"type":40,"tag":368,"props":1299,"children":1300},{},[1301],{"type":45,"value":771},{"type":40,"tag":368,"props":1303,"children":1304},{"class":370,"line":801},[1305],{"type":40,"tag":368,"props":1306,"children":1307},{},[1308],{"type":45,"value":1309},"        Cart.OnChange -= StateHasChanged;\n",{"type":40,"tag":368,"props":1311,"children":1312},{"class":370,"line":810},[1313],{"type":40,"tag":368,"props":1314,"children":1315},{},[1316],{"type":45,"value":816},{"type":40,"tag":368,"props":1318,"children":1319},{"class":370,"line":819},[1320],{"type":40,"tag":368,"props":1321,"children":1322},{},[1323],{"type":45,"value":441},{"type":40,"tag":55,"props":1325,"children":1326},{},[1327,1329,1335,1337,1343,1345,1350,1352,1357],{"type":45,"value":1328},"The simple ",{"type":40,"tag":61,"props":1330,"children":1332},{"className":1331},[],[1333],{"type":45,"value":1334},"Action OnChange",{"type":45,"value":1336}," pattern works when the event fires from the Blazor sync context (button click → ",{"type":40,"tag":61,"props":1338,"children":1340},{"className":1339},[],[1341],{"type":45,"value":1342},"Cart.Add(…)",{"type":45,"value":1344},"). If the event fires from ",{"type":40,"tag":476,"props":1346,"children":1347},{},[1348],{"type":45,"value":1349},"outside",{"type":45,"value":1351}," the sync context (timer, background task, SignalR hub), wrap in ",{"type":40,"tag":61,"props":1353,"children":1355},{"className":1354},[],[1356],{"type":45,"value":323},{"type":45,"value":1358},":",{"type":40,"tag":357,"props":1360,"children":1362},{"className":449,"code":1361,"language":451,"meta":362,"style":362},"private Action? _handler;\n\nprotected override void OnInitialized()\n{\n    _handler = () => InvokeAsync(StateHasChanged);\n    Cart.OnChange += _handler;\n}\n\npublic void Dispose() => Cart.OnChange -= _handler;\n",[1363],{"type":40,"tag":61,"props":1364,"children":1365},{"__ignoreMap":362},[1366,1374,1381,1389,1396,1404,1412,1419,1426],{"type":40,"tag":368,"props":1367,"children":1368},{"class":370,"line":371},[1369],{"type":40,"tag":368,"props":1370,"children":1371},{},[1372],{"type":45,"value":1373},"private Action? _handler;\n",{"type":40,"tag":368,"props":1375,"children":1376},{"class":370,"line":380},[1377],{"type":40,"tag":368,"props":1378,"children":1379},{"emptyLinePlaceholder":411},[1380],{"type":45,"value":414},{"type":40,"tag":368,"props":1382,"children":1383},{"class":370,"line":389},[1384],{"type":40,"tag":368,"props":1385,"children":1386},{},[1387],{"type":45,"value":1388},"protected override void OnInitialized()\n",{"type":40,"tag":368,"props":1390,"children":1391},{"class":370,"line":398},[1392],{"type":40,"tag":368,"props":1393,"children":1394},{},[1395],{"type":45,"value":627},{"type":40,"tag":368,"props":1397,"children":1398},{"class":370,"line":407},[1399],{"type":40,"tag":368,"props":1400,"children":1401},{},[1402],{"type":45,"value":1403},"    _handler = () => InvokeAsync(StateHasChanged);\n",{"type":40,"tag":368,"props":1405,"children":1406},{"class":370,"line":417},[1407],{"type":40,"tag":368,"props":1408,"children":1409},{},[1410],{"type":45,"value":1411},"    Cart.OnChange += _handler;\n",{"type":40,"tag":368,"props":1413,"children":1414},{"class":370,"line":426},[1415],{"type":40,"tag":368,"props":1416,"children":1417},{},[1418],{"type":45,"value":441},{"type":40,"tag":368,"props":1420,"children":1421},{"class":370,"line":435},[1422],{"type":40,"tag":368,"props":1423,"children":1424},{"emptyLinePlaceholder":411},[1425],{"type":45,"value":414},{"type":40,"tag":368,"props":1427,"children":1428},{"class":370,"line":756},[1429],{"type":40,"tag":368,"props":1430,"children":1431},{},[1432],{"type":45,"value":1433},"public void Dispose() => Cart.OnChange -= _handler;\n",{"type":40,"tag":55,"props":1435,"children":1436},{},[1437],{"type":45,"value":1438},"Store the delegate in a field so you can unsubscribe the exact same instance.",{"type":40,"tag":48,"props":1440,"children":1442},{"id":1441},"step-6-render-mode-and-service-lifetime-rules",[1443],{"type":45,"value":1444},"Step 6 — Render mode and service lifetime rules",{"type":40,"tag":1446,"props":1447,"children":1449},"h3",{"id":1448},"cascading-values-dont-cross-render-mode-boundaries",[1450],{"type":45,"value":1451},"Cascading values don't cross render mode boundaries",{"type":40,"tag":55,"props":1453,"children":1454},{},[1455,1457,1462,1464,1470,1472,1477,1479,1485],{"type":45,"value":1456},"A ",{"type":40,"tag":61,"props":1458,"children":1460},{"className":1459},[],[1461],{"type":45,"value":353},{"type":45,"value":1463}," placed in a static SSR layout (",{"type":40,"tag":61,"props":1465,"children":1467},{"className":1466},[],[1468],{"type":45,"value":1469},"MainLayout.razor",{"type":45,"value":1471}," when the layout renders statically) will ",{"type":40,"tag":476,"props":1473,"children":1474},{},[1475],{"type":45,"value":1476},"not",{"type":45,"value":1478}," reach interactive children. The interactive component sees ",{"type":40,"tag":61,"props":1480,"children":1482},{"className":1481},[],[1483],{"type":45,"value":1484},"null",{"type":45,"value":1486}," for the cascading parameter.",{"type":40,"tag":55,"props":1488,"children":1489},{},[1490,1495,1497,1502],{"type":40,"tag":476,"props":1491,"children":1492},{},[1493],{"type":45,"value":1494},"Fix:",{"type":45,"value":1496}," Use ",{"type":40,"tag":61,"props":1498,"children":1500},{"className":1499},[],[1501],{"type":45,"value":148},{"type":45,"value":1503}," 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.",{"type":40,"tag":1446,"props":1505,"children":1507},{"id":1506},"service-lifetime-on-server-vs-webassembly",[1508],{"type":45,"value":1509},"Service lifetime on Server vs WebAssembly",{"type":40,"tag":76,"props":1511,"children":1512},{},[1513,1534],{"type":40,"tag":80,"props":1514,"children":1515},{},[1516],{"type":40,"tag":84,"props":1517,"children":1518},{},[1519,1524,1529],{"type":40,"tag":88,"props":1520,"children":1521},{},[1522],{"type":45,"value":1523},"Lifetime",{"type":40,"tag":88,"props":1525,"children":1526},{},[1527],{"type":45,"value":1528},"Server",{"type":40,"tag":88,"props":1530,"children":1531},{},[1532],{"type":45,"value":1533},"WebAssembly",{"type":40,"tag":104,"props":1535,"children":1536},{},[1537,1558,1579],{"type":40,"tag":84,"props":1538,"children":1539},{},[1540,1548,1553],{"type":40,"tag":111,"props":1541,"children":1542},{},[1543],{"type":40,"tag":476,"props":1544,"children":1545},{},[1546],{"type":45,"value":1547},"Scoped",{"type":40,"tag":111,"props":1549,"children":1550},{},[1551],{"type":45,"value":1552},"Per circuit (per user connection)",{"type":40,"tag":111,"props":1554,"children":1555},{},[1556],{"type":45,"value":1557},"Per browser tab",{"type":40,"tag":84,"props":1559,"children":1560},{},[1561,1569,1574],{"type":40,"tag":111,"props":1562,"children":1563},{},[1564],{"type":40,"tag":476,"props":1565,"children":1566},{},[1567],{"type":45,"value":1568},"Singleton",{"type":40,"tag":111,"props":1570,"children":1571},{},[1572],{"type":45,"value":1573},"Shared across ALL users",{"type":40,"tag":111,"props":1575,"children":1576},{},[1577],{"type":45,"value":1578},"Per browser tab (safe)",{"type":40,"tag":84,"props":1580,"children":1581},{},[1582,1590,1595],{"type":40,"tag":111,"props":1583,"children":1584},{},[1585],{"type":40,"tag":476,"props":1586,"children":1587},{},[1588],{"type":45,"value":1589},"Transient",{"type":40,"tag":111,"props":1591,"children":1592},{},[1593],{"type":45,"value":1594},"New instance per injection",{"type":40,"tag":111,"props":1596,"children":1597},{},[1598],{"type":45,"value":1594},{"type":40,"tag":55,"props":1600,"children":1601},{},[1602,1604,1609,1611,1617],{"type":45,"value":1603},"On Server, ",{"type":40,"tag":476,"props":1605,"children":1606},{},[1607],{"type":45,"value":1608},"never store user-specific state in a singleton",{"type":45,"value":1610}," — every user's circuit shares the same singleton. One user's cart leaks into another's. Use ",{"type":40,"tag":61,"props":1612,"children":1614},{"className":1613},[],[1615],{"type":45,"value":1616},"AddScoped\u003CT>()",{"type":45,"value":596},{"type":40,"tag":55,"props":1619,"children":1620},{},[1621,1623,1627],{"type":45,"value":1622},"On WebAssembly, singletons are per-tab and safe. But code meant for ",{"type":40,"tag":476,"props":1624,"children":1625},{},[1626],{"type":45,"value":955},{"type":45,"value":1628}," Server and WebAssembly (Auto mode) must use scoped.",{"type":40,"tag":1446,"props":1630,"children":1632},{"id":1631},"autowebassembly-with-prerendering",[1633],{"type":45,"value":1634},"Auto\u002FWebAssembly with prerendering",{"type":40,"tag":55,"props":1636,"children":1637},{},[1638,1640,1645,1647,1652,1654,1659,1661,1667],{"type":45,"value":1639},"State services must be defined in the ",{"type":40,"tag":61,"props":1641,"children":1643},{"className":1642},[],[1644],{"type":45,"value":963},{"type":45,"value":1646}," project or a shared assembly — they cannot reference server-only types. Register the service in both ",{"type":40,"tag":61,"props":1648,"children":1650},{"className":1649},[],[1651],{"type":45,"value":257},{"type":45,"value":1653}," files. State created during prerender does not survive the switch to the interactive runtime. Use the ",{"type":40,"tag":61,"props":1655,"children":1657},{"className":1656},[],[1658],{"type":45,"value":216},{"type":45,"value":1660}," skill's ",{"type":40,"tag":61,"props":1662,"children":1664},{"className":1663},[],[1665],{"type":45,"value":1666},"[PersistentState]",{"type":45,"value":1668}," pattern to carry state across.",{"type":40,"tag":48,"props":1670,"children":1672},{"id":1671},"donts",[1673],{"type":45,"value":1674},"Don'ts",{"type":40,"tag":482,"props":1676,"children":1677},{},[1678,1688,1728,1746,1770,1807],{"type":40,"tag":230,"props":1679,"children":1680},{},[1681,1686],{"type":40,"tag":476,"props":1682,"children":1683},{},[1684],{"type":45,"value":1685},"Don't use a singleton for per-user state on Server",{"type":45,"value":1687}," — all circuits share it, leaking state between users.",{"type":40,"tag":230,"props":1689,"children":1690},{},[1691,1696,1698,1703,1705,1711,1713,1719,1720,1726],{"type":40,"tag":476,"props":1692,"children":1693},{},[1694],{"type":45,"value":1695},"Don't put all app state into one cascaded object",{"type":45,"value":1697}," — ",{"type":40,"tag":61,"props":1699,"children":1701},{"className":1700},[],[1702],{"type":45,"value":943},{"type":45,"value":1704}," re-renders ALL subscribers on every change. Separate concerns into distinct types (",{"type":40,"tag":61,"props":1706,"children":1708},{"className":1707},[],[1709],{"type":45,"value":1710},"ThemeState",{"type":45,"value":1712},", ",{"type":40,"tag":61,"props":1714,"children":1716},{"className":1715},[],[1717],{"type":45,"value":1718},"CartState",{"type":45,"value":1712},{"type":40,"tag":61,"props":1721,"children":1723},{"className":1722},[],[1724],{"type":45,"value":1725},"UserPreferences",{"type":45,"value":1727},").",{"type":40,"tag":230,"props":1729,"children":1730},{},[1731,1736,1738,1744],{"type":40,"tag":476,"props":1732,"children":1733},{},[1734],{"type":45,"value":1735},"Don't forget to unsubscribe",{"type":45,"value":1737}," — omitting ",{"type":40,"tag":61,"props":1739,"children":1741},{"className":1740},[],[1742],{"type":45,"value":1743},"Dispose",{"type":45,"value":1745}," on event subscriptions causes memory leaks that grow per-circuit.",{"type":40,"tag":230,"props":1747,"children":1748},{},[1749,1761,1763,1768],{"type":40,"tag":476,"props":1750,"children":1751},{},[1752,1754,1759],{"type":45,"value":1753},"Don't use ",{"type":40,"tag":61,"props":1755,"children":1757},{"className":1756},[],[1758],{"type":45,"value":353},{"type":45,"value":1760}," in a static layout expecting it to reach interactive children",{"type":45,"value":1762}," — it won't cross render mode boundaries. Use DI-registered ",{"type":40,"tag":61,"props":1764,"children":1766},{"className":1765},[],[1767],{"type":45,"value":148},{"type":45,"value":1769}," or scoped services.",{"type":40,"tag":230,"props":1771,"children":1772},{},[1773,1785,1787,1792,1793,1798,1800,1805],{"type":40,"tag":476,"props":1774,"children":1775},{},[1776,1777,1783],{"type":45,"value":1753},{"type":40,"tag":61,"props":1778,"children":1780},{"className":1779},[],[1781],{"type":45,"value":1782},"NavigationManager.Refresh(forceReload: true)",{"type":45,"value":1784}," to propagate cascading value changes",{"type":45,"value":1786}," — this destroys the circuit and forces a full page reload. Instead, inject ",{"type":40,"tag":61,"props":1788,"children":1790},{"className":1789},[],[1791],{"type":45,"value":148},{"type":45,"value":859},{"type":40,"tag":61,"props":1794,"children":1796},{"className":1795},[],[1797],{"type":45,"value":297},{"type":45,"value":1799}," to push updates to all ",{"type":40,"tag":61,"props":1801,"children":1803},{"className":1802},[],[1804],{"type":45,"value":284},{"type":45,"value":1806}," subscribers without a page reload.",{"type":40,"tag":230,"props":1808,"children":1809},{},[1810,1822,1824,1829,1831,1837],{"type":40,"tag":476,"props":1811,"children":1812},{},[1813,1815,1820],{"type":45,"value":1814},"Don't call ",{"type":40,"tag":61,"props":1816,"children":1818},{"className":1817},[],[1819],{"type":45,"value":315},{"type":45,"value":1821}," from a non-Blazor thread",{"type":45,"value":1823}," — wrap in ",{"type":40,"tag":61,"props":1825,"children":1827},{"className":1826},[],[1828],{"type":45,"value":323},{"type":45,"value":1830},". The framework throws ",{"type":40,"tag":61,"props":1832,"children":1834},{"className":1833},[],[1835],{"type":45,"value":1836},"InvalidOperationException: The current thread is not associated with the Dispatcher",{"type":45,"value":596},{"type":40,"tag":1839,"props":1840,"children":1841},"style",{},[1842],{"type":45,"value":1843},"html .light .shiki span {color: var(--shiki-light);background: var(--shiki-light-bg);font-style: var(--shiki-light-font-style);font-weight: var(--shiki-light-font-weight);text-decoration: var(--shiki-light-text-decoration);}html.light .shiki span {color: var(--shiki-light);background: var(--shiki-light-bg);font-style: var(--shiki-light-font-style);font-weight: var(--shiki-light-font-weight);text-decoration: var(--shiki-light-text-decoration);}html .default .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html.dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}",{"items":1845,"total":1950},[1846,1863,1878,1896,1910,1926,1936],{"slug":1847,"name":1847,"fn":1848,"description":1849,"org":1850,"tags":1851,"stars":22,"repoUrl":23,"updatedAt":1862},"analyzing-dotnet-performance","analyze .NET code for performance anti-patterns","Scans .NET code for ~50 performance anti-patterns across async, memory, strings, collections, LINQ, regex, serialization, and I\u002FO with tiered severity classification. Use when analyzing .NET code for optimization opportunities, reviewing hot paths, or auditing allocation-heavy patterns.",{"slug":8,"name":9,"logoUrl":10,"githubOrg":8},[1852,1853,1856,1859],{"name":13,"slug":14,"type":15},{"name":1854,"slug":1855,"type":15},"Code Analysis","code-analysis",{"name":1857,"slug":1858,"type":15},"Debugging","debugging",{"name":1860,"slug":1861,"type":15},"Performance","performance","2026-07-12T08:23:25.400375",{"slug":1864,"name":1864,"fn":1865,"description":1866,"org":1867,"tags":1868,"stars":22,"repoUrl":23,"updatedAt":1877},"android-tombstone-symbolication","symbolicate .NET runtime frames in Android tombstones","Symbolicate the .NET runtime frames in an Android tombstone file. Extracts BuildIds and PC offsets from the native backtrace, downloads debug symbols from the Microsoft symbol server, and runs llvm-symbolizer to produce function names with source file and line numbers. USE FOR triaging a .NET MAUI or Mono Android app crash from a tombstone, resolving native backtrace frames in libmonosgen-2.0.so or libcoreclr.so to .NET runtime source code, or investigating SIGABRT, SIGSEGV, or other native signals originating from the .NET runtime on Android. DO NOT USE FOR pure Java\u002FKotlin crashes, managed .NET exceptions that are already captured in logcat, or iOS crash logs. INVOKES Symbolicate-Tombstone.ps1 script, llvm-symbolizer, Microsoft symbol server.",{"slug":8,"name":9,"logoUrl":10,"githubOrg":8},[1869,1870,1873,1874],{"name":13,"slug":14,"type":15},{"name":1871,"slug":1872,"type":15},"Android","android",{"name":1857,"slug":1858,"type":15},{"name":1875,"slug":1876,"type":15},"Microsoft","microsoft","2026-07-12T08:23:21.595572",{"slug":1879,"name":1879,"fn":1880,"description":1881,"org":1882,"tags":1883,"stars":22,"repoUrl":23,"updatedAt":1895},"apple-crash-symbolication","symbolicate .NET runtime frames in crash logs","Symbolicate .NET runtime frames in Apple platform .ips crash logs (iOS, tvOS, Mac Catalyst, macOS). Extracts UUIDs and addresses from the native backtrace, locates dSYM debug symbols, and runs atos to produce function names with source file and line numbers. Automatically downloads .dwarf symbols from the Microsoft symbol server using Mach-O UUIDs. USE FOR triaging a .NET MAUI or Mono app crash from an .ips file on any Apple platform, resolving native backtrace frames in libcoreclr or libmonosgen-2.0 to .NET runtime source code, retrieving .ips crash logs from a connected iOS device or iPhone, or investigating EXC_CRASH, EXC_BAD_ACCESS, SIGABRT, or SIGSEGV originating from the .NET runtime. DO NOT USE FOR pure Swift\u002FObjective-C crashes with no .NET components, or Android tombstone files. INVOKES Symbolicate-Crash.ps1 script, atos, dwarfdump, idevicecrashreport.",{"slug":8,"name":9,"logoUrl":10,"githubOrg":8},[1884,1885,1886,1889,1892],{"name":13,"slug":14,"type":15},{"name":1857,"slug":1858,"type":15},{"name":1887,"slug":1888,"type":15},"iOS","ios",{"name":1890,"slug":1891,"type":15},"macOS","macos",{"name":1893,"slug":1894,"type":15},"Observability","observability","2026-07-12T08:23:20.369986",{"slug":1897,"name":1897,"fn":1898,"description":1899,"org":1900,"tags":1901,"stars":22,"repoUrl":23,"updatedAt":1909},"assertion-quality","evaluate assertion quality in test suites","Analyzes the variety and depth of assertions across test suites in any language. Use when the user asks to evaluate assertion quality, find shallow tests, identify assertion-free tests (no assertions or only trivial ones like Assert.IsNotNull \u002F toBeTruthy()), flag self-referential or tautological assertions, measure assertion diversity, or audit whether tests verify different facets of behavior. Polyglot: .NET, Python, TS\u002FJS, Java, Go, Ruby, Rust, Swift, Kotlin, PowerShell, C++. DO NOT USE FOR: writing new tests (use code-testing-agent \u002F writing-mstest-tests), mutation reasoning about whether tests would catch a bug (use test-gap-analysis), or a general severity-ranked anti-pattern audit (use test-anti-patterns), fixing or rewriting assertions, or writing, fixing, or modernizing MSTest tests, assertions, or attributes (use writing-mstest-tests).",{"slug":8,"name":9,"logoUrl":10,"githubOrg":8},[1902,1903,1906],{"name":1854,"slug":1855,"type":15},{"name":1904,"slug":1905,"type":15},"QA","qa",{"name":1907,"slug":1908,"type":15},"Testing","testing","2026-07-12T08:23:51.277743",{"slug":208,"name":208,"fn":1911,"description":1912,"org":1913,"tags":1914,"stars":22,"repoUrl":23,"updatedAt":1925},"create and review Blazor components","Create or review Blazor components (.razor files) with correct architecture. USE FOR: writing new Blazor components that do NOT involve JavaScript interop, implementing parameters and EventCallback, RenderFragment slots, component lifecycle (OnInitializedAsync, OnParametersSet), async patterns, IAsyncDisposable, CancellationToken, CSS isolation, code-behind. DO NOT USE FOR: creating new projects (use create-blazor-project), JavaScript interop or calling browser APIs from Blazor (use use-js-interop), forms and validation (use collect-user-input), prerendering issues (use support-prerendering), HTTP data fetching patterns (use fetch-and-send-data), coordinating state between unrelated components (use coordinate-components).\n",{"slug":8,"name":9,"logoUrl":10,"githubOrg":8},[1915,1916,1917,1919,1922],{"name":13,"slug":14,"type":15},{"name":17,"slug":18,"type":15},{"name":1918,"slug":451,"type":15},"C#",{"name":1920,"slug":1921,"type":15},"UI Components","ui-components",{"name":1923,"slug":1924,"type":15},"Web Development","web-development","2026-07-15T06:03:29.216359",{"slug":1927,"name":1927,"fn":1928,"description":1929,"org":1930,"tags":1931,"stars":22,"repoUrl":23,"updatedAt":1935},"binlog-failure-analysis","analyze MSBuild binary logs","Analyze MSBuild binary logs to diagnose build failures. USE FOR: build errors that are unclear from console output, diagnosing cascading failures across multi-project builds, tracing MSBuild target execution order, and generally any MSBuild build issues. Requires an existing .binlog file. DO NOT USE FOR: generating binlogs (use binlog-generation), non-MSBuild build systems.",{"slug":8,"name":9,"logoUrl":10,"githubOrg":8},[1932,1933,1934],{"name":1854,"slug":1855,"type":15},{"name":1857,"slug":1858,"type":15},{"name":1875,"slug":1876,"type":15},"2026-07-12T08:21:34.637923",{"slug":1937,"name":1937,"fn":1938,"description":1939,"org":1940,"tags":1941,"stars":22,"repoUrl":23,"updatedAt":1949},"binlog-generation","generate MSBuild binary logs for diagnostics","Generate MSBuild binary logs (binlogs) for build diagnostics and analysis. USE FOR: adding \u002Fbl:{} to any dotnet build, test, pack, publish, or restore command to capture a full build execution trace, prerequisite for binlog-failure-analysis and build-perf-diagnostics skills, enabling post-build investigation of errors or performance. Requires MSBuild 17.8+ \u002F .NET 8 SDK+ for {} placeholder; PowerShell needs -bl:{{}}. DO NOT USE FOR: non-MSBuild build systems (npm, Maven, CMake), analyzing an existing binlog (use binlog-failure-analysis instead).",{"slug":8,"name":9,"logoUrl":10,"githubOrg":8},[1942,1945,1946],{"name":1943,"slug":1944,"type":15},"Build","build",{"name":1857,"slug":1858,"type":15},{"name":1947,"slug":1948,"type":15},"Engineering","engineering","2026-07-19T05:38:19.340791",96,{"items":1952,"total":2057},[1953,1965,1972,1979,1987,1993,2001,2007,2013,2023,2036,2047],{"slug":1954,"name":1954,"fn":1955,"description":1956,"org":1957,"tags":1958,"stars":1962,"repoUrl":1963,"updatedAt":1964},"multithreaded-task-migration","migrate MSBuild tasks to multithreaded mode","Guide for migrating MSBuild tasks to multithreaded mode support, including compatibility red-team review. Use this when converting tasks to thread-safe versions, implementing IMultiThreadableTask, adding TaskEnvironment support, or auditing migrations for behavioral compatibility.",{"slug":8,"name":9,"logoUrl":10,"githubOrg":8},[1959,1960,1961],{"name":13,"slug":14,"type":15},{"name":1947,"slug":1948,"type":15},{"name":1860,"slug":1861,"type":15},5535,"https:\u002F\u002Fgithub.com\u002Fdotnet\u002Fmsbuild","2026-07-22T05:37:33.965588",{"slug":1847,"name":1847,"fn":1848,"description":1849,"org":1966,"tags":1967,"stars":22,"repoUrl":23,"updatedAt":1862},{"slug":8,"name":9,"logoUrl":10,"githubOrg":8},[1968,1969,1970,1971],{"name":13,"slug":14,"type":15},{"name":1854,"slug":1855,"type":15},{"name":1857,"slug":1858,"type":15},{"name":1860,"slug":1861,"type":15},{"slug":1864,"name":1864,"fn":1865,"description":1866,"org":1973,"tags":1974,"stars":22,"repoUrl":23,"updatedAt":1877},{"slug":8,"name":9,"logoUrl":10,"githubOrg":8},[1975,1976,1977,1978],{"name":13,"slug":14,"type":15},{"name":1871,"slug":1872,"type":15},{"name":1857,"slug":1858,"type":15},{"name":1875,"slug":1876,"type":15},{"slug":1879,"name":1879,"fn":1880,"description":1881,"org":1980,"tags":1981,"stars":22,"repoUrl":23,"updatedAt":1895},{"slug":8,"name":9,"logoUrl":10,"githubOrg":8},[1982,1983,1984,1985,1986],{"name":13,"slug":14,"type":15},{"name":1857,"slug":1858,"type":15},{"name":1887,"slug":1888,"type":15},{"name":1890,"slug":1891,"type":15},{"name":1893,"slug":1894,"type":15},{"slug":1897,"name":1897,"fn":1898,"description":1899,"org":1988,"tags":1989,"stars":22,"repoUrl":23,"updatedAt":1909},{"slug":8,"name":9,"logoUrl":10,"githubOrg":8},[1990,1991,1992],{"name":1854,"slug":1855,"type":15},{"name":1904,"slug":1905,"type":15},{"name":1907,"slug":1908,"type":15},{"slug":208,"name":208,"fn":1911,"description":1912,"org":1994,"tags":1995,"stars":22,"repoUrl":23,"updatedAt":1925},{"slug":8,"name":9,"logoUrl":10,"githubOrg":8},[1996,1997,1998,1999,2000],{"name":13,"slug":14,"type":15},{"name":17,"slug":18,"type":15},{"name":1918,"slug":451,"type":15},{"name":1920,"slug":1921,"type":15},{"name":1923,"slug":1924,"type":15},{"slug":1927,"name":1927,"fn":1928,"description":1929,"org":2002,"tags":2003,"stars":22,"repoUrl":23,"updatedAt":1935},{"slug":8,"name":9,"logoUrl":10,"githubOrg":8},[2004,2005,2006],{"name":1854,"slug":1855,"type":15},{"name":1857,"slug":1858,"type":15},{"name":1875,"slug":1876,"type":15},{"slug":1937,"name":1937,"fn":1938,"description":1939,"org":2008,"tags":2009,"stars":22,"repoUrl":23,"updatedAt":1949},{"slug":8,"name":9,"logoUrl":10,"githubOrg":8},[2010,2011,2012],{"name":1943,"slug":1944,"type":15},{"name":1857,"slug":1858,"type":15},{"name":1947,"slug":1948,"type":15},{"slug":2014,"name":2014,"fn":2015,"description":2016,"org":2017,"tags":2018,"stars":22,"repoUrl":23,"updatedAt":2022},"build-parallelism","optimize MSBuild build parallelism","Diagnose and fix under-parallelized MSBuild builds. USE WHEN a multi-project solution build is slower than expected, doesn't speed up when you add cores, pegs a single core while others idle, or you want to know why `-m` isn't helping. Note: `\u002Fmaxcpucount` default is 1 (sequential) — always pass `-m` for parallel builds. Covers finding the critical path (longest serial ProjectReference chain), graph build (`\u002Fgraph`), BuildInParallel, and solution filters (`.slnf`). DO NOT USE FOR: single-project builds, incremental issues (use incremental-build), compilation slowness inside one project (use build-perf-diagnostics), non-MSBuild build systems.",{"slug":8,"name":9,"logoUrl":10,"githubOrg":8},[2019,2020,2021],{"name":13,"slug":14,"type":15},{"name":1947,"slug":1948,"type":15},{"name":1860,"slug":1861,"type":15},"2026-07-19T05:38:18.364937",{"slug":2024,"name":2024,"fn":2025,"description":2026,"org":2027,"tags":2028,"stars":22,"repoUrl":23,"updatedAt":2035},"build-perf-baseline","establish and optimize build performance baselines","Establish build performance baselines and apply systematic optimization techniques. USE FOR: diagnosing slow builds, establishing before\u002Fafter measurements (cold, warm, no-op scenarios), applying optimization strategies like MSBuild Server, static graph builds, artifacts output, and dependency graph trimming. Start here before diving into build-perf-diagnostics, incremental-build, or build-parallelism. DO NOT USE FOR: non-MSBuild build systems, detailed bottleneck analysis (use build-perf-diagnostics after baselining).",{"slug":8,"name":9,"logoUrl":10,"githubOrg":8},[2029,2030,2033,2034],{"name":1947,"slug":1948,"type":15},{"name":2031,"slug":2032,"type":15},"Monitoring","monitoring",{"name":1860,"slug":1861,"type":15},{"name":1907,"slug":1908,"type":15},"2026-07-12T08:21:35.865649",{"slug":2037,"name":2037,"fn":2038,"description":2039,"org":2040,"tags":2041,"stars":22,"repoUrl":23,"updatedAt":2046},"build-perf-diagnostics","diagnose MSBuild build performance bottlenecks","Diagnose MSBuild build performance bottlenecks using binary log analysis. USE FOR: identifying why builds are slow by analyzing binlog performance summaries, detecting ResolveAssemblyReference (RAR) taking >5s, Roslyn analyzers consuming >30% of Csc time, single targets dominating >50% of build time, node utilization below 80%, excessive Copy tasks, NuGet restore running every build. Covers timeline analysis, Target\u002FTask Performance Summary interpretation, and 7 common bottleneck categories. Use after build-perf-baseline has established measurements. DO NOT USE FOR: establishing initial baselines (use build-perf-baseline first), fixing incremental build issues (use incremental-build), parallelism tuning (use build-parallelism), non-MSBuild build systems.",{"slug":8,"name":9,"logoUrl":10,"githubOrg":8},[2042,2043,2044,2045],{"name":13,"slug":14,"type":15},{"name":1857,"slug":1858,"type":15},{"name":1947,"slug":1948,"type":15},{"name":1860,"slug":1861,"type":15},"2026-07-12T08:21:40.961722",{"slug":2048,"name":2048,"fn":2049,"description":2050,"org":2051,"tags":2052,"stars":22,"repoUrl":23,"updatedAt":2056},"check-bin-obj-clash","detect MSBuild output path conflicts","Detects MSBuild projects with conflicting OutputPath or IntermediateOutputPath. USE FOR: builds failing with 'Cannot create a file when that file already exists', 'The process cannot access the file because it is being used by another process', intermittent build failures that succeed on retry, or missing\u002Foverwritten outputs in multi-project or multi-targeting builds where bin\u002Fobj (or project.assets.json) collide. Common causes: shared OutputPath, missing AppendTargetFrameworkToOutputPath, extra global properties (e.g. PublishReadyToRun), or SetTargetFramework on a ProjectReference to a single-targeting project. DO NOT USE FOR: file access errors unrelated to MSBuild (OS-level locking), single-project single-TFM builds, non-MSBuild build systems.",{"slug":8,"name":9,"logoUrl":10,"githubOrg":8},[2053,2054,2055],{"name":1857,"slug":1858,"type":15},{"name":1947,"slug":1948,"type":15},{"name":1904,"slug":1905,"type":15},"2026-07-19T05:38:14.336279",144]