Zonit.Extensions.Website
10.0.0-preview.13
See the version list below for details.
dotnet add package Zonit.Extensions.Website --version 10.0.0-preview.13
NuGet\Install-Package Zonit.Extensions.Website -Version 10.0.0-preview.13
<PackageReference Include="Zonit.Extensions.Website" Version="10.0.0-preview.13" />
<PackageVersion Include="Zonit.Extensions.Website" Version="10.0.0-preview.13" />
<PackageReference Include="Zonit.Extensions.Website" />
paket add Zonit.Extensions.Website --version 10.0.0-preview.13
#r "nuget: Zonit.Extensions.Website, 10.0.0-preview.13"
#:package Zonit.Extensions.Website@10.0.0-preview.13
#addin nuget:?package=Zonit.Extensions.Website&version=10.0.0-preview.13&prerelease
#tool nuget:?package=Zonit.Extensions.Website&version=10.0.0-preview.13&prerelease
Zonit.Extensions.Website
The Blazor / ASP.NET Core host for the Zonit stack. It owns the request pipeline, the Razor base components, and the plumbing that carries per-request state into an interactive circuit.
dotnet add package Zonit.Extensions.Website
Targets net10.0. Version documented here: 10.0.0-preview.10.
Two calls, two phases
AddWebsite() configures the container. UseWebsite<TApp>() mounts a Site — a URL prefix
with its own middleware branch, its own MapRazorComponents<TApp>(), and its own subset of
registered areas. A host can mount as many Sites as it likes.
var builder = WebApplication.CreateBuilder(args);
builder.Services.AddWebsite(o =>
{
o.Url = "https://example.com";
o.AddArea<HomeArea>();
o.AddArea<AuthArea>();
});
var app = builder.Build();
// Non-root mounts FIRST — see the ordering rule below.
app.UseWebsite<App>("/admin", o =>
{
o.Permission = "admin";
o.AddArea<AuthArea>();
});
app.UseWebsite<App>("/", o =>
{
o.AddArea<HomeArea>();
o.AddArea<AuthArea>();
});
app.Run();
AddWebsite() already registers the five domain cores — AddCulturesExtension(),
AddAuthExtension(), AddOrganizationsExtension(), AddProjectsExtension(),
AddTenantsExtension() — plus navigation, breadcrumbs, toasts, cookies and layouts. Calling any of
them yourself is a double registration, not a requirement.
UseWebsite needs no companion Use* calls. Each Site branch installs its own
UseRouting / UseAuthentication / UseAuthorization / UseAntiforgery and the whole Zonit
middleware chain (cookies → session → workspace → project → tenant → culture). There is no
app.UseAuthExtension() and no manual UseMiddleware<CultureMiddleware>() — those middlewares are
internal and are wired for you.
Declare non-root mounts before the root mount. The root branch ends in a terminal
UseEndpoints, so any MapWhen branch registered after it is unreachable. Getting this backwards
used to fail silently with a 405 on /<sub>/_blazor/negotiate; it now throws at startup with a
message telling you to reorder.
One tag in App.razor
@using Zonit.Extensions.Website.Hydration
<body>
<WebsiteHydrator @rendermode="@RenderMode.InteractiveServer" />
<Routes @rendermode="@RenderMode.InteractiveServer" />
<script src="_framework/blazor.web.js"></script>
</body>
The HTTP-request scope that runs the middleware is not the SignalR circuit scope that owns
interactive components, so without this the circuit starts anonymous, with default culture and no
workspace — after a perfectly authenticated SSR pass. <WebsiteHydrator /> aggregates every
registered IPersistentStateProvider and round-trips their snapshots through
PersistentComponentState. It needs a render mode, or it only ever runs on the SSR half.
A page
@page "/orders"
@inherits PageViewBase<List<OrderRow>>
@attribute [RequirePermission("orders.read")]
<h1>@T("Orders for {0}", Workspace.Organization.Name)</h1>
@if (IsLoading)
{
<p>@T("Loading…")</p>
}
else
{
@foreach (var row in Model ?? [])
{
<p>@row.Customer</p>
}
}
<button @onclick="@(() => Toast.AddSuccess(T("Saved")))">@T("Save")</button>
@code {
protected override Task<List<OrderRow>?> LoadAsync(CancellationToken cancellationToken)
=> _orders.ListAsync(cancellationToken);
}
Put the usings in _Imports.razor once — Zonit.Extensions (the value objects: UrlPath,
Title, Permission), Zonit.Extensions.Website (the base classes and UI providers) and
Zonit.Extensions.Website.Authentication ([RequirePermission], [RequireRole]).
PageViewBase<T> gives you Model, IsLoading, a LoadAsync that re-runs when the workspace /
catalog / tenant changes, and prerender→circuit model persistence. PageEditBase<T> adds an
EditContext, DataAnnotations validation translated through ICultureProvider, change tracking,
duplicate-submit protection and per-field [AutoSave]. Both inherit the injected provider surface
(Culture, Workspace, Catalog, Tenant, Authenticated, Toast, Cookie,
BreadcrumbsProvider) and T() / TM() for translation.
Every overridable lifecycle method takes a CancellationToken, and that token is genuinely
cancelled when the component is disposed — override the token overload, not the framework's
parameterless one.
What else is in the box
- Areas —
IWebsiteAreaplug-ins that contribute Razor components, navigation, middleware hooks and minimal-API endpoints, mountable on any number of Sites. - Layouts — string-keyed layout registry with
[LayoutKey("…")],[NoLayout]and a runtimeLayoutKeyoverride. - Permissions —
[RequirePermission]/[RequireRole]with a synthetic policy provider, the"Zonit"cookie authentication scheme, and a BlazorAuthenticationStateProvider. - UI services — navigation, breadcrumbs, toasts (
<ZonitToasts />) and cookies, plusUrlPath.ToHref(), which is mandatory for links under a non-root mount. - A source generator that emits AOT-safe view-model metadata for every
Tyou use withPageViewBase<T>/PageEditBase<T>.
Upgrading from 10.0.0-preview.9
- Five components were deleted in commit
1cfc6d8:<ZonitCulturesExtension />,<ZonitIdentityExtension />,<ZonitOrganizationsExtension />,<ZonitProjectsExtension />,<ZonitCookiesExtension />. Replace all of them with one<WebsiteHydrator />. - The source generator no longer emits a
JsonSerializerContext. In preview.9 it did, and because Roslyn does not chain generators the emitted partial was never completed — every consumer build failed withCS0534.ViewModelMetadata<T>.JsonTypeInfois gone with it. INavigationProvideris now scoped, not singleton. Injecting it into a singletonIHostedServicethrows at startup; takeIServiceScopeFactoryand create a scope. Additions still land in the process-wide store.- Component cancellation tokens now really cancel on dispose. Fire-and-forget work riding on a
page's token will abort when the user navigates away — pass
CancellationToken.Noneor move it to a service. PageViewBase<T>no longer overridesOnRefreshChangeAsync, which removes a duplicateLoadAsyncon every provider change.- Model persistence and all hydration bridges no-op instead of throwing when
JsonSerializer.IsReflectionEnabledByDefaultisfalse— which the SDK sets for anyPublishTrimmedpublish. Read the AOT notes before publishing trimmed.
Trimming and Native AOT
The assembly ships IsTrimmable / IsAotCompatible. Two members warn at your call site:
AddWebsite and ExtensionsBase.Options<T>(). Deriving from the page base classes warns about
nothing.
Under PublishTrimmed (and therefore PublishAot) prerender→circuit hydration and
PageViewBase model persistence silently switch themselves off — the app keeps working, but
circuits start anonymous with default culture. Set
<JsonSerializerIsReflectionEnabledByDefault>true</JsonSerializerIsReflectionEnabledByDefault> to
keep them, or publish untrimmed. The full, honest account is in the aot document below.
Documentation
Installing the package writes these into your repository at build time (.zonit/extensions/website/,
plus editor-specific copies under .cursor/rules/, .github/instructions/ and .claude/skills/):
| Document | Covers |
|---|---|
hosting.md |
AddWebsite, UseWebsite<TApp>, several Sites in one app, SiteOptions, mount ordering |
areas.md |
IWebsiteArea, IWebsiteServices, navigation contributions, the three pipeline hooks |
pages.md |
PageBase / PageViewBase<T> / PageEditBase<T>, cancellation, EditForm wiring, auto-save |
layouts.md |
AddWebsiteLayout, [LayoutKey], [NoLayout], ZonitRouteView, precedence |
hydration.md |
WebsiteHydrator, the built-in bridges, writing your own IPersistentStateProvider |
permissions.md |
[RequirePermission], [RequireRole], the claim contract, the session cookie |
ui-services.md |
Navigation, breadcrumbs, toasts, cookies, UrlPath.ToHref() |
aot.md |
What is genuinely trim/AOT-safe, what is annotated, what turns off |
License
MIT.
| Product | Versions Compatible and additional computed target framework versions. |
|---|---|
| .NET | net10.0 is compatible. net10.0-android was computed. net10.0-browser was computed. net10.0-ios was computed. net10.0-maccatalyst was computed. net10.0-macos was computed. net10.0-tvos was computed. net10.0-windows was computed. |
-
net10.0
- Zonit.Extensions (>= 10.0.0-preview.13)
- Zonit.Extensions.Auth (>= 10.0.0-preview.13)
- Zonit.Extensions.Cultures (>= 10.0.0-preview.13)
- Zonit.Extensions.Organizations (>= 10.0.0-preview.13)
- Zonit.Extensions.Projects (>= 10.0.0-preview.13)
- Zonit.Extensions.Tenants (>= 10.0.0-preview.13)
NuGet packages (5)
Showing the top 5 NuGet packages that depend on Zonit.Extensions.Website:
| Package | Downloads |
|---|---|
|
Zonit.Services.Dashboard
Package Description |
|
|
Zonit.Services.Manager
Package Description |
|
|
Zonit.SDK.Website
Package Description |
|
|
Zonit.Dashboard
Zonit dashboard — single-project admin UI built on Zonit.Extensions.Website. Provides drawer/toolbar extension slots, dynamic theme switching, and a MudBlazor-based main layout registered into the website layout registry as "Dashboard.Main". |
|
|
Zonit.Extensions.Website.Sitemaps
Sitemap generation for Zonit Website hosts. Plug-ins declare an ISitemapSource returning records; the package owns paging, the 50 000-URL and 50 MB limits, the sitemap index, hreflang alternates and the XML. |
GitHub repositories
This package is not used by any popular GitHub repositories.
| Version | Downloads | Last Updated |
|---|---|---|
| 10.0.0-preview.17 | 0 | 8/9/2026 |
| 10.0.0-preview.16 | 0 | 8/9/2026 |
| 10.0.0-preview.15 | 32 | 8/7/2026 |
| 10.0.0-preview.14 | 42 | 8/6/2026 |
| 10.0.0-preview.13 | 38 | 8/6/2026 |
| 10.0.0-preview.12 | 35 | 8/6/2026 |
| 10.0.0-preview.11 | 63 | 8/4/2026 |
| 10.0.0-preview.10 | 57 | 8/3/2026 |
| 10.0.0-preview.9 | 69 | 5/16/2026 |
| 10.0.0-preview.6 | 60 | 5/15/2026 |
| 10.0.0-preview.2 | 65 | 5/12/2026 |
| 10.0.0-preview.1 | 70 | 5/8/2026 |
| 0.2.12 | 130 | 5/8/2026 |
| 0.2.11 | 125 | 4/19/2026 |
| 0.2.10 | 140 | 1/22/2026 |
| 0.2.9 | 130 | 1/21/2026 |
| 0.2.8 | 126 | 1/21/2026 |
| 0.2.7 | 138 | 1/16/2026 |
| 0.2.6 | 130 | 1/15/2026 |
| 0.2.4 | 128 | 1/15/2026 |