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

support-prerendering — скилл для Claude Code: функции и установка

Настраивает интерактивные компоненты Blazor для работы с пререндерингом. Помогает устранить повторную загрузку данных, мерцание интерфейса и ошибки null при переходе в интерактивный режим.

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

Что делает support-prerendering

Скилл учит агента работать с интерактивными компонентами Blazor в режимах Server, WebAssembly и Auto, где пререндеринг включён по умолчанию. При таком сценарии метод OnInitializedAsync выполняется дважды: сначала на сервере для формирования статичного HTML, а затем при подключении интерактивного рантайма. Инструкция показывает, как сохранять состояние между этими фазами через атрибут [PersistentState] или сервис PersistentComponentState, предотвращая мерцание интерфейса и повторные запросы в базу данных.

Внутри описано отключение пререндеринга через параметр prerender: false на уровне отдельного компонента или в файле App.razor. Скилл также разбирает изоляцию страниц атрибутом [ExcludeFromInteractiveRouting] для прямого доступа к HttpContext. Отдельное внимание уделено клиентским сервисам из проекта .Client, которые вызывают исключения на сервере, если не зарегистрированы в основном контейнере зависимостей.

Вызовы JS interop внутри OnInitializedAsync гарантированно приведут к сбою, потому что JavaScript во время серверного пререндеринга ещё недоступен. Браузерные вызовы нужно переносить в OnAfterRenderAsync или закрывать проверкой флага RendererInfo.IsInteractive. Если проект полностью работает в режиме Static SSR, скилл не требуется.

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

  • устранить двойную загрузку данных при пререндеринге Blazor
  • сохранить состояние между серверным пререндером и клиентом через [PersistentState]
  • убрать мерцание интерфейса при передаче управления интерактивному рантайму
  • отключить пререндеринг для конкретного компонента или всего приложения
  • изолировать страницы с доступом к HttpContext через [ExcludeFromInteractiveRouting]

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

  • У меня в компоненте Blazor OnInitializedAsync срабатывает дважды и дублирует запрос, как сохранить состояние?
  • Отключи пререндеринг для компонента MyChart в режиме InteractiveServerRenderMode
  • Добавь атрибут [ExcludeFromInteractiveRouting] на страницу /privacy и обнови App.razor

Как установить support-prerendering

1Через Skills CLI (Vercel)

npx skills add dotnet/skills --skill support-prerendering -a claude-code

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

~/.claude/skills/support-prerendering/SKILL.md

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

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

Вопросы о support-prerendering

Как установить support-prerendering в Claude Code?

Выполните команду npx skills add dotnet/skills --skill support-prerendering -a claude-code. Она загрузит и зарегистрирует скилл в конфигурации Claude Code.

Что делает скилл support-prerendering?

Он предоставляет агенту правила и паттерны для корректной работы Blazor с пререндерингом: сохранение состояния, отключение пререндера и обход ограничений серверного рендеринга.

Почему во время пререндеринга нельзя вызывать JS interop в OnInitializedAsync?

Пререндеринг выполняется на стороне сервера до того, как браузер загрузит и запустит интерактивный рантайм. Вызовы JavaScript в этот момент падают с ошибкой, поэтому их нужно переносить в OnAfterRenderAsync.

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

SKILL.md · MIT
---
license: MIT
name: support-prerendering
description: Make interactive Blazor components work correctly with prerendering. USE FOR fixing duplicate data loads, UI flicker during prerender-to-interactive handoff, null references during prerender, persisting state across prerender, disabling prerendering, excluding pages from interactive routing, or detecting whether a component is currently prerendering. DO NOT USE for choosing which render mode to use (see create-blazor-project) or general component authoring (see author-component).
---

# Support Prerendering

## How Prerendering Works

Prerendering is **on by default** for all interactive render modes. The server renders the component as static HTML and ships it to the browser immediately. Then the interactive runtime (Server/WebAssembly) loads and re-renders the component with full interactivity.

This means:
- `OnInitializedAsync` runs **twice** — once during prerender (static), once when the interactive runtime attaches.
- `OnAfterRenderAsync` is **NOT** called during prerender — only after the interactive render.
- Internal navigation between interactive pages (interactive routing) **skips prerendering** — prerendering only happens on full page loads.

## Step 1 — Read the Project's AGENTS.md

Check the project's `AGENTS.md` for the **Interactivity Mode** and **Interactivity Scope**:

| Mode | Prerendering applies? |
|------|----------------------|
| None (Static SSR) | No — there's no interactive handoff |
| Server | Yes |
| WebAssembly | Yes |
| Auto | Yes |

If the mode is `None`, this skill doesn't apply.

## Persist State Across Prerender → Interactive

The most common prerendering problem: data loaded in `OnInitializedAsync` during prerender is thrown away and re-fetched when the interactive runtime attaches. This causes flicker and duplicate API/DB calls.

### Recommended: `[PersistentState]` attribute

Annotate properties to automatically serialize during prerender and restore on interactive activation:

```razor
@page "/forecasts"
@rendermode InteractiveServer

<h1>Weather</h1>

@if (Forecasts is null)
{
    <p>Loading...</p>
}
else
{
    @foreach (var f in Forecasts)
    {
        <p>@f.Date: @f.TemperatureC°C</p>
    }
}

@code {
    [PersistentState]
    public WeatherForecast[]? Forecasts { get; set; }

    protected override async Task OnInitializedAsync()
    {
        Forecasts ??= await ForecastService.GetForecastsAsync();
    }
}
```

The `??=` pattern is critical — it means "only fetch if the property wasn't already restored from prerender state."

### Multiple instances of the same component

When the same component type appears multiple times, use `@key` to disambiguate state:

```razor
@foreach (var item in items)
{
    <ItemCard @key="item.Id" />
}
```

### Advanced: `PersistentComponentState` service

For complex scenarios (dynamic keys, custom serialization), use the imperative API:

```csharp
@inject PersistentComponentState ApplicationState

@code {
    private List<Order>? orders;

    protected override async Task OnInitializedAsync()
    {
        ApplicationState.RegisterOnPersisting(PersistOrders);

        if (!ApplicationState.TryTakeFromJson<List<Order>>("orders", out var restored))
        {
            orders = await OrderService.GetOrdersAsync();
        }
        else
        {
            orders = restored;
        }
    }

    private Task PersistOrders()
    {
        ApplicationState.PersistAsJson("orders", orders);
        return Task.CompletedTask;
    }
}
```

## Disable Prerendering

Disable prerendering when a component depends on browser APIs immediately or when the prerender+interactive double render causes problems you can't solve with `[PersistentState]`.

### On a component definition

```razor
@rendermode @(new InteractiveServerRenderMode(prerender: false))
```

Replace `InteractiveServerRenderMode` with `InteractiveWebAssemblyRenderMode` or `InteractiveAutoRenderMode` as needed.

### On a component instance

```razor
<MyChart @rendermode="new InteractiveServerRenderMode(prerender: false)" />
```

### On the entire app

In `App.razor`:

```razor
<HeadOutlet @rendermode="new InteractiveServerRenderMode(prerender: false)" />
<Routes @rendermode="new InteractiveServerRenderMode(prerender: false)" />
```

Note: A parent's prerendering setting overrides children. If `<Routes>` disables prerendering, individual pages cannot re-enable it.

## Exclude Pages from Interactive Routing

In a globally interactive app, some pages may need `HttpContext` (cookies, request headers, response status codes). These pages must render via static SSR, not inside the interactive runtime.

Use `[ExcludeFromInteractiveRouting]`:

```razor
@page "/privacy"
@attribute [ExcludeFromInteractiveRouting]

<h1>Privacy Policy</h1>
```

This forces a **full page reload** when navigating to this page, exiting interactive routing. The page renders as static SSR with full `HttpContext` access.

In `App.razor`, conditionally apply the render mode:

```razor
<!DOCTYPE html>
<html>
<head>
    <HeadOutlet @rendermode="RenderModeForPage" />
</head>
<body>
    <Routes @rendermode="RenderModeForPage" />
    <script src="_framework/blazor.web.js"></script>
</body>
</html>

@code {
    [CascadingParameter]
    public HttpContext HttpContext { get; set; } = default!;

    private IComponentRenderMode? RenderModeForPage =>
        HttpContext.AcceptsInteractiveRouting() ? InteractiveServer : null;
}
```

Replace `InteractiveServer` with the app's configured render mode.

## Detect Prerender vs Interactive at Runtime

Use `RendererInfo` to guard code that should only run interactively:

```csharp
protected override async Task OnInitializedAsync()
{
    if (RendererInfo.IsInteractive)
    {
        // Only runs during the interactive render, not during prerender
        await StartSignalRConnection();
    }
}
```

`RendererInfo` properties:
- `IsInteractive` — `false` during prerender, `true` after interactive runtime attaches
- `Name` — `"Static"` during prerender, `"Server"` or `"WebAssembly"` when interactive

## Client Services Fail During Prerender

Components in the `.Client` project prerender on the server. Services registered only in the client `Program.cs` (e.g., `IWebAssemblyHostEnvironment`) won't be available during prerender.

Fix by one of:
1. **Register a matching service on the server** — both `Program.cs` files provide the service
2. **Make the service optional** — use constructor injection with a nullable default: `public MyComponent(IMyService? svc = null)`
3. **Create a service abstraction** — interface in `.Client`, implementations in both projects
4. **Disable prerendering** for that component

## Don'ts

- Don't call JS interop in `OnInitializedAsync` — JS isn't available during prerender. Use `OnAfterRenderAsync(firstRender)`.
- Don't assume `OnInitializedAsync` runs once — it runs twice with prerendering. Always use `[PersistentState]` or `??=` guards.
- Don't use `HttpContext` in interactive components — it's only available during the static prerender, not during the interactive lifetime. Use `[ExcludeFromInteractiveRouting]` for pages that need it.
- Don't disable prerendering as a first resort — it hurts perceived load time and SEO. Use `[PersistentState]` to preserve state instead.

© dotnet · MIT · dotnet/skills

Похожие скиллы

Всё по теме «Фронтенд и UI» →
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установок blazor Mindrally Скилл задаёт агенту правила разработки на Blazor: жизненный цикл компонентов, привязка данных, управление состоянием и оптимизация рендеринга. CX Codex +1 798установок playwright-blazor-testing Aaronontheweb Скилл учит агента писать end-to-end тесты для Blazor Server и WebAssembly на Playwright: навигация, формы, авторизация, селекторы. CX Codex +1 537установок angular-ssr analogjs Скилл для задач Angular SSR, гидратации и пререндеринга. Он устарел: текущая версия больше не поддерживается. CX Codex +1 3 900установок

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

test-analysis-extensions Справочный каталог файлов с таблицами для анализа тестов: маркеры, ассерты, пропуски, паттерны ожиданий. Агент читает нужный файл под язык проекта. CX Codex +1 3 100установок find-untested-sources Находит файлы исходного кода без тестов через статический анализ AST без сборки проекта. Пригодится агенту перед генерацией тестов, чтобы составить точный список пробелов. CX Codex +1 3 100установок fetch-and-send-data Помогает загружать и отправлять данные через API в Blazor-компонентах. Нужен для настройки HttpClient, состояний загрузки и обработки ошибок. CX Codex +1 3 100установок setup-local-sdk Ставит .NET SDK в папку .dotnet/ внутри проекта и подключает его через global.json, не трогая системную установку. CX Codex +1 2 900установок target-authoring Скилл для написания кастомных MSBuild-таргетов: подсказывает канонические паттерны и ловит анти-паттерны вроде перезаписи цепочек DependsOn. CX Codex +1 3 000установок template-comparison Скилл сравнивает два и больше шаблонов dotnet new в таблице и подсказывает, какой брать под вашу задачу. Пригодится, когда выбираете между webapi и webapp или blazor и blazorwasm. CX Codex +1 2 800установок template-smart-defaults Скилл подсказывает агенту, какие связанные параметры подставить в dotnet new, когда часть флагов не задана, и не трогает то, что выбрал пользователь. CX Codex +1 2 800установок migrate-xunit-to-mstest Переводит тестовые проекты .NET с xUnit v2 или v3 на MSTest v4 с сохранением запуска через VSTest или MTP. Пригодится при стандартизации тестов в репозитории. CX Codex +1 2 900установок

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

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