Skip to content

ServerIntegration

Edgar Mesquita edited this page Aug 7, 2026 · 8 revisions

Server Integration

eQuantic.UI integrates with ASP.NET Core through a fluent API for service registration, middleware configuration, and HTML shell customization.

Quick Start

var builder = WebApplication.CreateBuilder(args);

builder.Services.AddUI(options =>
{
    options.ScanAssembly(typeof(Program).Assembly)
           .WithSsr()
           .UseTailwind()              // Theme
           .UseLucideIcons()           // Icons
           .UseChartJs()               // Charts
           .UseApexCharts()            // Charts
           .ConfigureHtmlShell(shell =>
           {
               shell.SetTitle("My App")
                    .SetHtmlClass("dark")
                    .AddHeadTag("<meta name=\"theme-color\" content=\"#3b82f6\">");
           });
});

var app = builder.Build();

app.UseStaticFiles();
app.UseServerActions();                // Server Actions middleware
app.MapUI();                           // SPA routing & package endpoints

app.Run();

AddUI

AddUI() is the main entry point that registers all core services:

builder.Services.AddUI(options => { ... });

What it registers:

  • UIOptions (singleton) - Configuration
  • IServerActionRegistry - Scans assemblies for [ServerAction] methods
  • IServerActionAuthorizationService - Authorization for server actions
  • IServerRenderingService - SSR rendering engine
  • IAppTheme - Default eQuantic theme (overridden by AddTailwind())
  • IComponentAssetProvider<T> - Auto-scanned from assemblies (see Assets)
  • SignalR services

UIOptions

Fluent configuration API for the UI framework.

ScanAssembly

Scans an assembly for [Page] components, [ServerAction] methods, and IComponentAssetProvider<T> implementations.

options.ScanAssembly(typeof(Program).Assembly);

Multiple assemblies can be scanned:

options.ScanAssembly(typeof(Program).Assembly)
       .ScanAssembly(typeof(SharedComponents).Assembly);

WithSsr

Enables or disables Server-Side Rendering globally. Default is true.

options.WithSsr();           // Enable (default)
options.WithSsr(false);      // Disable

Individual pages can opt-out:

[Page("/interactive", DisableSsr = true)]
public class InteractivePage : StatefulComponent { }

WithAssetProvider<T>

Explicitly registers an IComponentAssetProvider<T> implementation. Useful for providers from external assemblies not covered by ScanAssembly.

options.WithAssetProvider<ChartJsAssetProvider>();

See Assets for details on the asset provider system.

ConfigureHtmlShell

Configures the HTML template that wraps all pages.

options.ConfigureHtmlShell(shell =>
{
    shell.SetTitle("My App")
         .SetHtmlClass("dark")
         .SetBaseStyles("body { margin: 0; }")
         .AddHeadTag("<link rel=\"icon\" href=\"/favicon.ico\">")
         .AddHeadTag("<meta name=\"viewport\" content=\"width=device-width, initial-scale=1\">");
});

Properties

Property Type Default Description
EnableSsr bool true Global SSR toggle
EnableDefaultCss bool true Inject default eQuantic CSS (set false with Tailwind)
HtmlShell HtmlShellOptions - HTML template configuration

HtmlShellOptions

Method Description
SetTitle(string) Page <title>
SetHtmlClass(string) Class on <html> element (e.g., "dark")
SetBaseStyles(string) Base <style> block
AddHeadTag(string) Raw HTML tag injected into <head>

Middleware

UseServerActions

Registers the Server Actions middleware for handling RPC calls from the browser.

app.UseServerActions();

Server Actions are methods marked with [ServerAction] that execute server-side and return results to the client:

[ServerAction]
public async Task<List<Todo>> LoadTodos()
{
    using var db = new AppDbContext();
    return await db.Todos.ToListAsync();
}

MapUI

Maps SPA routing — serves the HTML shell for all unmatched routes, enabling client-side navigation.

app.MapUI();

UseTailwind

Enables Tailwind CSS services and dynamic endpoints (theme scripts, dark mode scripts).

builder.Services.AddUI(options =>
{
    options.UseTailwind();
});

UseChartJs / UseApexCharts

Enables services and CDN script endpoints for chart libraries.

builder.Services.AddUI(options =>
{
    options.UseChartJs()
           .UseApexCharts();
});

Hot reload (web)

On in Development (or forced with options.HotReload = true): the server watches the app's *.cs, re-runs the SDK's own eqc target on a save, and tells every connected browser to refresh over SSE (/_equantic/hmr). ~5s from save to pixels with a warm MSBuild.

It had shipped broken in three independent ways, each silent — found by driving the whole circuit in a real browser (the server half worked; the browser half had never been run):

What Symptom
the refreshed page HYDRATED the stale SSR it reloaded and showed the OLD pixels — the one thing you check. The SSR comes from the server's still-running assembly; only the JS was rebuilt. A reload triggered by hot reload now MOUNTS (renders client-side with the new code) instead of adopting the old DOM.
the reload marker was only written when legacy _state existed every write-once page (no _state bag) kept hydrating — same stale pixels
EventSource.onerror → close() any transient drop and hot reload was silently dead for that tab. Removed: the browser reconnects by itself, and abandons the prod 404 on its own.

Plus two robustness holes on the server: the parked SSE request never spoke (idle-dropped by Kestrel and proxies after a couple of minutes — a : ping comment now flows every 20s), and the rebuild read its pipes only after waiting (a failure with >64KB of output deadlocked until the 2-minute timeout — the pipes are read concurrently now, and a successful rebuild logs its duration).

Limit worth knowing: the server's own C# (server actions, SSR bodies) still runs the old assembly — the eqc target refreshes the CLIENT. For server-side edits, run under dotnet watch run: .NET hot reload patches the running server in-process, and this pipeline keeps handling the client half.

Server-Side Rendering (SSR)

When SSR is enabled, the framework:

  1. Finds the matching [Page] component for the route
  2. Creates the component instance (with DI support)
  3. Collects asset dependencies (see Assets)
  4. Collects SEO metadata (see below)
  5. Renders the component tree to HTML
  6. Serializes state for client-side hydration
  7. Serves the complete HTML page

SEO & Metadata

Components implement IHandleMetadata for dynamic SEO:

public class BlogPost : StatelessComponent, IHandleMetadata
{
    public void ConfigureMetadata(SeoBuilder seo)
    {
        seo.Title("Blog Post Title")
           .Description("A summary of the post...")
           .Canonical("https://example.com/blog/post")
           .OpenGraph("type", "article")
           .Twitter("card", "summary_large_image");
    }
}

SeoBuilder methods:

Method Description
Title(string) Page title
Description(string) Meta description
Canonical(string) Canonical URL
Image(string, string?) OG image
Keywords(string) Meta keywords
Robots(bool, bool) Index/follow directives
OpenGraph(string, string) OG property
Twitter(string, string) Twitter card property

The recommended way to register UI features is within the AddUI fluent block:

builder.Services.AddUI(options =>
{
    options
        .UseTailwind()
        .UseLucideIcons()
        .UseChartJs()
        .UseApexCharts()
        .ScanAssembly(typeof(Program).Assembly);
});

Middleware order:

app.UseStaticFiles();
app.UseRouting();
app.UseServerActions();    // Before MapUI
app.MapUI();               // SPA fallback + Package endpoints (last)

Clone this wiki locally