Skip to content

PortalService: "no <BbPortalHost /> is registered in this render context" reproduces with host+dialog in the same component (no layout involved) #545

Description

@gabrielttech

Environment

  • BlazorBlueprint.Components: 3.17.0
  • BlazorBlueprint.Icons.Lucide: 2.0.2
  • .NET 8, Blazor Web App (Server), global interactivity:
    builder.Services.AddRazorComponents()
        .AddInteractiveServerComponents();
    builder.Services.AddBlazorBlueprintComponents();
    ...
    app.MapRazorComponents<App>()
        .AddInteractiveServerRenderMode();
    and in App.razor:
    <HeadOutlet @rendermode="@InteractiveServer" />
    ...
    <Routes @rendermode="@InteractiveServer" />
  • The host app also has MudBlazor 8.0 installed alongside (migrating a large existing app to BlazorBlueprint incrementally, page by page). Mentioning this only because it's the one thing that differs from a from-scratch template — see the "possibly relevant" note at the bottom.

What happens

Opening a <BbDialog> never renders <BbDialogContent>. The trigger button's own state does update correctly (aria-expanded flips to true, aria-controls points at the (never-created) content id), but nothing appears, and this is logged every time:

warn: BlazorBlueprint.Primitives.Services.PortalService[1]
      BlazorBlueprint: no <BbPortalHost /> is registered in this render context, so Dialog, Select, Popover and other portal-based components cannot render. If your layout has no <BbPortalHost />, add one to MainLayout.razor. If it already has one, then it is rendering somewhere this code cannot reach it. In a Blazor Web App that is usually because the layout is static while the page is interactive: a routed page's layout inherits the page's render mode only when interactivity is applied globally. Either set @rendermode on <Routes /> in App.razor, or render <BbPortalHost />, <BbToastProvider /> and <BbDialogProvider /> as interactive islands with their own @rendermode. Under InteractiveWebAssembly the host must additionally live in an assembly the client project loads — a layout in the server project cannot run in the browser, however it is marked.

Why the documented cause doesn't seem to fit

I found #479 / #493 first and assumed it was the same "layout renders statically while the page is interactive" issue. I don't think it is, for two reasons:

  1. Global interactivity is already applied exactly as shown above (the standard "Global" template shape), and other interactive behavior in the same layout works normally — click handlers fire, IJSRuntime calls from the layout's OnAfterRenderAsync succeed, server-side state changes re-render the UI. So the layout is demonstrably not static.

  2. I reproduced it with <BbPortalHost /> and <BbDialog> in the same file, no layout involved at all:

    @page "/blueprint-test-2"
    @layout EmptyLayout   @* LayoutComponentBase, just renders @Body, nothing else *@
    @using BlazorBlueprint.Components
    @using BlazorBlueprint.Primitives
    
    <BbDialog>
        <BbDialogTrigger AsChild>
            <BbButton>Open</BbButton>
        </BbDialogTrigger>
        <BbDialogContent>
            <BbDialogHeader>
                <BbDialogTitle>Test</BbDialogTitle>
            </BbDialogHeader>
        </BbDialogContent>
    </BbDialog>
    
    <BbPortalHost />

    Same warning, same non-rendering result. With host and trigger/content literally in the same component tree, on the same page, there's no layout/page render-mode boundary left to blame — so I don't think this is the Timed out waiting for PortalHost to render in WebAssembly #479/fix: PortalHost warning told people to add a host they already had #493 scenario, or at least not only that.

What I've ruled out

  • Package version: already on latest stable (3.17.0); 4.0.0 is only betas on NuGet right now.
  • @using scoping: confirmed BlazorBlueprint.Components/BlazorBlueprint.Primitives are in scope everywhere Bb* tags are used (had to make these local @usings instead of global, for an unrelated reason — MudBlazor and BlazorBlueprint.Components share several type names: DialogOptions, DialogResult, IDialogReference, ButtonType, InputType, SortDirection, DateRange. A global @using BlazorBlueprint.Components makes all of those ambiguous everywhere the host app also has @using MudBlazor. Scoping the using per-file fixed the ambiguity; unrelated to the portal issue, but flagging in case it's useful to others hitting both libraries at once).
  • Adding @rendermode="InteractiveServer" explicitly to <BbPortalHost /> (one of the two suggested workarounds in the current warning text) — throws RZ10023: Attribute '@rendermode' is only valid when used on a component at compile time in this already-globally-interactive context, so that particular remedy doesn't apply here (or I'm missing the right way to phrase it).
  • Retrying the click (in case it was a first-render race) — same result every time, not a timing issue.

What I haven't been able to do

Reduce this to a from-scratch minimal repro project — this was found while integrating BlazorBlueprint into an existing, fairly large Blazor Server app, and I ran out of time to scaffold a clean isolated repro today. Happy to do that next if it'd help, or to run any diagnostic you'd want me to try in the current app in the meantime.

Possibly relevant

Given the co-installed MudBlazor and the CSS @layer interaction I ran into separately (Tailwind-compiled utilities losing to MudBlazor's unscoped button{} reset because unlayered CSS always wins over layered CSS regardless of specificity), I wonder if AddMudServices() being registered alongside AddBlazorBlueprintComponents() could be creating two separate scoped-service registrations of something PortalService depends on, or otherwise interfering with DI in a way that isn't visible from the outside. I don't have a way to verify this without access to PortalService's source, but wanted to mention it in case it points somewhere useful.

Thanks for the library — happy to provide more logs/details or try suggested fixes.

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions