Ekioo.com runs on Blazor and .NET 10 in production. The site serves bilingual content, a blog, project pages and sitemaps. This article explains the render architecture selected for those needs and the boundaries that matter in practice.


The .NET 10 Render Pipeline Used by Ekioo

In the .NET 10 application model used by Ekioo, static rendering (Static SSR) is the baseline. Interactivity can be applied locally, inherited from a parent, or configured globally.

Concretely:

  • Pages are rendered as Static SSR by default: the server sends their content as HTML without requiring JavaScript for interactivity.
  • Components that need interactivity use an effective render mode selected locally, inherited from a parent, or configured globally.
  • Components using InteractiveServer are handled through a real-time connection with the browser; static components don't retain component state after the response.

For Ekioo's mostly static publishing workload and isolated interactions, I prefer this division of responsibilities.

Blazor .NET 10 render pipeline: effective render mode selects Static SSR or interactivity


Interactive Blazor SSR: The Boundary to Manage

Static SSR components render a response without retaining component state. Components whose effective mode is InteractiveServer use a real-time browser connection and a server circuit. A local directive can select that mode, but a component can also inherit it from an interactive parent or receive it from global configuration.

The Pitfalls of Mixing Static and Interactive

This hybrid model is powerful but requires discipline.

Pitfall 1: complex parameters don't cross the boundary

A static parent can pass JSON-serializable parameters to an interactive component. Parameters that can't be serialized, such as render fragments or other delegate-typed values, aren't supported across that boundary.

The solution: pass IDs or keys, and let the interactive component fetch the data itself.

Pitfall 2: prerendering by default

Interactive components are prerendered by default. Their initialization logic can therefore run once during prerendering and again when the component becomes interactive.

For work that must only happen after interactivity starts, use OnAfterRenderAsync(bool firstRender), which isn't called during prerendering. For data loaded during both phases, persist prerendered state or disable prerendering where appropriate.


Streaming Rendering: Real Behavior and Use Cases

What Streaming SSR Actually Does

Streaming rendering (enabled with @attribute [StreamRendering]) lets Blazor send HTML progressively as components render, without waiting for the whole page to be ready.

Concretely: if a component is loading slow data (a database query, an API call), Blazor can send the rest of the page immediately and "stream" that component's content when its data becomes available.

From the browser's perspective: the page starts displaying very quickly, with placeholders for pending parts, then the final content replaces the placeholders with no visible reload.

Use Cases on ekioo.com

On ekioo.com, the blog lists articles from markdown files on disk, so I don't use streaming for that list. I would consider it for a section backed by slower data because Blazor can render placeholders and patch the final content later.

Pitfalls to Know

Because the final content patches the DOM after the first response fragments, I design Ekioo's scripts to tolerate those later updates. In my interfaces, I also reserve stable dimensions for placeholders.


Static SSR and SEO: What It Really Changes

Static SSR: Complete HTML, Zero JavaScript Required

With pure Static SSR, the server generates the page content as HTML. No JavaScript is needed to read that content.

For ekioo.com, the practical benefit is straightforward: article content is present in the initial HTML response. For streamed interfaces, I choose dimensionally stable placeholders.

Sitemap Update Metadata

Ekioo.com exposes two sitemaps: /sitemap.xml (FR + EN) and /sitemap-content.xml (raw markdown). The lastmod dates are pulled directly from the YAML frontmatter of the markdown files.

Google documents lastmod as the date of the last significant page update and says it may use the value when it is consistently accurate. Ekioo generates these dates from the content metadata stored with each Markdown file.


A Concrete Example: Bilingual Navigation on ekioo.com

A case that illustrates the SSR model's constraints well:

Ekioo.com is bilingual (FR/EN). Language selection comes only from the URL: no cookie, no Accept-Language detection and no automatic language redirect. Google recommends separate locale-specific URLs and warns that automatic language redirects can prevent crawlers from finding every version.

Each localized content page declares two routes:

@page "/blog/{Slug}"
@page "/en/blog/{Slug}"

Pages and components that need the language inject the LanguageState service:

@inject LanguageState Lang

In Ekioo's Static SSR implementation, each URL produces an independent request and the application derives the language from that request path.


Verdict for Ekioo

Ekioo's constraints are mostly static content, bilingual publishing, SEO metadata and a C# codebase. For this project, Static SSR by default and interactivity by exception keep the rendering model aligned with the content model.

That is a project-specific architecture choice, not a universal recommendation. Applications with different interaction, state or team constraints should evaluate the available render modes against their own requirements.