← UI Rendering & SSR

UI RENDERING & SSR FEATURE

Reactive Analytics

Each panel updates when its own job finishes, so the dashboard feels live without turning into a client-side orchestration layer.

Feature Guide

A quick orientation block that answers the essential questions: what this feature does, how it works, why it matters, and the key concepts behind it.

What this does

A live dashboard can be assembled from independent server snapshots, so panels update progressively without waiting for one giant frontend state sync.

How it works

Analytics jobs write their own snapshots, ReactiveAnalyticsSlot re-renders on refreshInterval, and each panel reflects the latest server state as soon as it is available.

Why it matters

This keeps dashboards honest and incremental. The UI feels live, but the architecture stays SSR-first instead of drifting into client-side orchestration code.

Key concepts

DemoAnalyticsSnapshot
Stores one analytics slice so panels can update independently.
ReactiveAnalyticsSlot
Deferred slot that turns the latest snapshot set into a live dashboard panel surface.
SSR-first live UI
Panels update as server-rendered HTML instead of a client app rebuilding the dashboard.

Progressive Dashboard

Panels wake up as their own server snapshots arrive

Each analytics job owns one slice of truth, and the live slot composes the dashboard from those server snapshots instead of a frontend sync loop.

3 panels updated independently
0 client merge layer required
SSR rendering model from first byte to live refresh

Page Views

— Latest snapshot

No data yet

Conversion Rate

— Latest snapshot

No data yet

Top Products

— Latest snapshot

No data yet

multi-job snapshots independent panel refresh refreshInterval: 5 SSR-first live UI

Verified against Semitexa Ultimate 2026.09.19.1020

Reactive Analytics

Each panel updates when its own job finishes, so the dashboard feels live without turning into a client-side orchestration layer.

How it works

Each analytics metric is produced by an independent background job. Each job writes its own snapshot to server storage. A deferred slot with refreshInterval: 5 holds its SSE connection open while the server re-renders the dashboard HTML every five seconds and pushes it. Each render reads the latest available snapshot per metric and composes the full dashboard from those server-authoritative values.

The dashboard assembles from server snapshots instead of a frontend sync loop. Panels that have data show values; panels waiting on a job show a pending state. The page model stays consistent throughout.

Key mechanisms

  • multi-job snapshots — each panel reads from its own independent job output rather than a single aggregated API call.
  • independent panel refresh — a new snapshot from one job updates that panel without requiring the other panels to re-fetch.
  • refreshInterval: 5 — the server re-renders and pushes every five seconds; the browser never polls.
  • SSR-first live UI — the dashboard is server-rendered from first byte to live refresh; no client merge layer required.

Why this matters

Analytics dashboards with multiple data sources often require a frontend orchestration layer that fetches from multiple endpoints, merges results, and manages per-panel loading states. Composing the dashboard in the slot render function instead means the server owns that merge and the page receives fully composed HTML on each refresh.

© Linus Torvalds: "Talk is cheap. Show me the code."

ReactiveAnalyticsSlot Secondary page region
<?phpdeclare(strict_types=1);use Examples\Rendering\Philosophy\Support\JobService;use Semitexa\Core\Attribute\InjectAsReadonly;use Semitexa\Ssr\Attribute\AsSlotHandler;use Semitexa\Ssr\Attribute\AsSlotResource;use Semitexa\Ssr\Application\Service\Http\Response\HtmlSlotResponse;#[AsSlotResource(    handle: 'imports_dashboard',    slot: 'job_status',    deferred: true,    refreshInterval: 3,)]final class ImportJobStatusSlot extends HtmlSlotResponse{    public function withStatus(string $status): self    {        return $this->with('status', $status);    }    public function withProgress(int $progress): self    {        return $this->with('progress', $progress);    }}#[AsSlotHandler(slot: ImportJobStatusSlot::class)]final class ImportJobStatusSlotHandler{    #[InjectAsReadonly]    protected JobService $jobs;    public function handle(ImportJobStatusSlot $slot): ImportJobStatusSlot    {        $run = $this->jobs->latestImportRun();        return $slot            ->withStatus($run->status())            ->withProgress($run->progressPercent());    }}// The slot keeps re-rendering server truth. No second client-side state machine is required.

How it works

Analytics jobs write their own snapshots, ReactiveAnalyticsSlot re-renders on refreshInterval, and each panel reflects the latest server state as soon as it is available.

Why it matters

This keeps dashboards honest and incremental. The UI feels live, but the architecture stays SSR-first instead of drifting into client-side orchestration code.

Key concepts

DemoAnalyticsSnapshot
Stores one analytics slice so panels can update independently.
ReactiveAnalyticsSlot
Deferred slot that turns the latest snapshot set into a live dashboard panel surface.
SSR-first live UI
Panels update as server-rendered HTML instead of a client app rebuilding the dashboard.

Support Semitexa
Built for developers who prefer control over magic. Your support helps keep it fast, open, and evolving.

Donate via PayPal