← UI Rendering & SSR

UI RENDERING & SSR FEATURE

SSR Philosophy

Semitexa SSR is not “render once on the server and then improvise”. It is a coherent rendering system that refuses both kinds of drift: backend HTML plus frontend survival code, and fake “data vs presentation” separation where templates quietly become data loaders.

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

Semitexa SSR is not a thin “server pre-render” layer. It is a rendering architecture where the page contract, page regions, deferred delivery, live refresh, and component interaction all stay inside one coherent server-owned model.

How it works

Resource DTOs define the page boundary before Twig renders a single field, slot resources give regions their own response pipeline, deferred blocks stream later HTML instead of triggering client takeover, reactive slots keep re-rendering server truth, and interactive components stay scoped through tiny component-owned JavaScript instead of a page-level UI framework dependency.

Why it matters

Most stacks keep the phrase SSR but abandon the idea the moment the page becomes dynamic. Templates start reshaping data, partials start guessing context, and eventually view files begin pulling from storage or external APIs directly. Semitexa treats that drift as a framework problem, not a code review suggestion. It is opinionated because it is trying to preserve one rendering story, one presentation boundary, and one source of truth even when the page becomes late, live, or interactive.

Key concepts

one rendering story
The same conceptual model survives first paint, deferred delivery, live refresh, and component interaction.
server-owned truth
The browser displays HTML, but the authoritative rendering contract and state interpretation stay on the server side.
presentation boundary
Templates consume already-shaped response data instead of fetching, reshaping, or interpreting domain data on their own.
late HTML
Deferred content arrives after the shell, but it still arrives as server-rendered HTML rather than a client-side redraw.
reactive SSR
A live region can keep refreshing from server truth while remaining part of the SSR page model.
interactive SSR component
A server-rendered component can declare backend event contracts without turning into a mini SPA island.
framework-free enhancement
Client behavior may exist, but it remains small, scoped, and component-owned instead of becoming a second UI framework layer.

Rendering Manifesto

Semitexa SSR keeps the page whole and refuses a hidden client framework layer.

The page, its regions, its late content, its live refresh cycle, and even its interactive component events all stay inside one rendering architecture. That architecture may use JavaScript, but it does not surrender the page to React, Alpine, or another UI framework the moment interactivity appears. If your product decision is to make a client UI framework the primary rendering runtime, the cleaner path is to stop using Semitexa SSR for that surface and consume Semitexa through its REST or GraphQL API instead.

Why most SSR stacks stop too early

  • “SSR” usually means the first paint is server-rendered, but the moment the page becomes dynamic, teams quietly switch to a second frontend architecture.
  • At the same time, templates start accumulating query logic, ad hoc reshaping, and service calls because nobody protected the presentation boundary structurally.
  • Page regions become partials with hidden context, live widgets become little apps, and interaction drifts into custom fetch glue.
  • Soon the page is “SSR” only in name, while React, Alpine, or bespoke client glue becomes the real owner of interaction and state interpretation.
  • The result works, but the architecture has already split into two competing truths: server render for the shell, client orchestration for everything interesting.
1 rendering story from first byte to live update
1 presentation boundary before Twig sees data
0 client-side page takeover points
0 required UI framework layers to make the page interactive
0 template-level database or API fetching as an accepted pattern
6 core Semitexa SSR primitives composing one model

Typical Split SSR

Server-render the shell, then smuggle in a client framework

The page starts on the server, but regions, refresh loops, and interactions slowly migrate into React, Alpine, or ad hoc client orchestration.

You keep the phrase SSR, but the browser quietly becomes the second rendering authority.

Semitexa SSR

One rendering architecture, end to end

Page resource, slot resources, deferred delivery, reactive refresh, component event bridging, and tiny component-owned scripts all remain part of the same server-owned rendering model.

The UI can become late, live, or interactive without making a client framework the hidden second half of the system.

Semitexa SSR pillar What it protects
Page Contract Resource DTOs make the page response explicit before Twig sees a single field.
Presentation Boundary Templates consume prepared data instead of querying storage, calling APIs, or inventing new view-side mapping rules.
Region Contract Slots are real resources with their own pipeline, not fragment glue.
Late HTML Deferred blocks stream SSR HTML later instead of handing control to a client renderer.
Live HTML Reactive slots refresh from server truth without inventing a second state machine.
Interactive HTML Components can now dispatch backend events while staying in the SSR component model.
Framework-Free JS When JavaScript is needed, it stays as small component-owned enhancement rather than a mandatory UI framework layer.
one rendering story HtmlResponse Presentation boundary Deferred SSR Framework-free enhancement

Verified against Semitexa Ultimate 2026.09.19.1020

SSR Philosophy

Semitexa SSR is not "render once on the server and then improvise". It is a coherent rendering system that refuses both kinds of drift: backend HTML plus frontend survival code, and fake "data vs presentation" separation where templates quietly become data loaders.

The problem

"SSR" usually means the first paint is server-rendered, but the moment the page becomes dynamic, teams quietly switch to a second frontend architecture. At the same time, templates start accumulating query logic, ad hoc reshaping, and service calls because nobody protected the presentation boundary structurally.

Page regions become partials with hidden context, live widgets become little apps, and interaction drifts into custom fetch glue. Soon the page is "SSR" only in name, while React, Alpine, or bespoke client glue becomes the real owner of interaction and state interpretation.

The seven pillars

  • Page Contract — Resource DTOs make the page response explicit before Twig sees a single field.
  • Presentation Boundary — Templates consume prepared data instead of querying storage, calling APIs, or inventing new view-side mapping rules.
  • Region Contract — Slots are real resources with their own pipeline, not fragment glue.
  • Late HTML — Deferred blocks stream SSR HTML later instead of handing control to a client renderer.
  • Live HTML — Reactive slots refresh from server truth without inventing a second state machine.
  • Interactive HTML — Components can now dispatch backend events while staying in the SSR component model.
  • Framework-Free JS — When JavaScript is needed, it stays as small component-owned enhancement rather than a mandatory UI framework layer.

The rules

  1. The page response must be explicit before template rendering begins.
  2. Templates are presentation surfaces, not a place to fetch from databases, hit APIs, or smuggle handler logic into Twig.
  3. A region is a resource with a pipeline, not a partial with lucky context.
  4. Deferred does not mean "replace SSR with client rendering later". It means late server HTML.
  5. Reactive does not mean "invent a mini SPA for one widget". It means the server keeps re-rendering truth.
  6. Interactivity does not require escaping into bespoke API glue. Components can declare backend event contracts directly.
  7. JavaScript may enhance the page, but Semitexa refuses to require React, Alpine, Angular, or another client framework as the second rendering layer.
  8. Assets, scripts, and SEO stay inside the same rendering system instead of becoming a parallel deployment concern.

What Semitexa SSR refuses to become

Anti-pattern Semitexa answer
"SSR for the first paint, client app for everything real." Semitexa keeps deferred and reactive regions inside the same HTML pipeline.
"The template can just query what it needs." Semitexa treats that as presentation-boundary failure. Handlers and resource DTOs must shape data before Twig sees it.
"This region is just a partial, pass whatever data it seems to need." Semitexa promotes regions to slot resources with an explicit render contract.
"The widget is live, so we need a second state architecture." Semitexa refreshes the slot from server truth and swaps HTML in place.
"Once the page becomes interactive, we obviously need React or Alpine." Semitexa rejects that as a default assumption. Small component-owned scripts are enough when the server still owns rendering truth.

© Harold Abelson: "Programs must be written for people to read, and only incidentally for machines to execute."

Page Resource Presentation boundary
<?phpdeclare(strict_types=1);use App\Domain\Catalog\CatalogInterface;use App\Domain\Product;use Semitexa\Core\Attribute\AsPublicPayload;use Semitexa\Core\Attribute\AsPayloadHandler;use Semitexa\Core\Attribute\InjectAsReadonly;use Semitexa\Core\Attribute\AsResource;use Semitexa\Core\Contract\TypedHandlerInterface;use Semitexa\Core\Exception\NotFoundException;use Semitexa\Core\Exception\ValidationException;use Semitexa\Ssr\Application\Service\Http\Response\HtmlResponse;#[AsPublicPayload(    path: '/products/{slug}',    methods: ['GET'],    responseWith: ProductPageResource::class,    requirements: ['slug' => '[a-z0-9-]+'],)]final class ProductPagePayload{    protected string $slug = '';    public function getSlug(): string    {        return $this->slug;    }    public function setSlug(string $slug): void    {        $slug = strtolower(trim($slug));        if ($slug === '') {            throw new ValidationException(['slug' => ['Product slug is required.']]);        }        if (preg_match('/^[a-z0-9-]+$/', $slug) !== 1) {            throw new ValidationException(['slug' => ['Product slug may only contain lowercase letters, numbers, and dashes.']]);        }        $this->slug = $slug;    }}#[AsPayloadHandler(payload: ProductPagePayload::class, resource: ProductPageResource::class)]final class ProductPageHandler implements TypedHandlerInterface{    #[InjectAsReadonly]    private CatalogInterface $catalog;    public function handle(ProductPagePayload $payload, ProductPageResource $resource): ProductPageResource    {        $product = $this->catalog->getBySlug($payload->getSlug());        if ($product === null) {            throw new NotFoundException('Product', $payload->getSlug());        }        return $resource            ->withProduct($product)            ->withHeroActions(['buy', 'bookmark'])            ->withInventoryState($product->inventoryState());    }}#[AsResource(handle: 'product_page', template: '@shop/pages/product.html.twig')]final class ProductPageResource extends HtmlResponse{    public function withProduct(Product $product): self    {        return $this->with('product', $product);    }    public function withInventoryState(string $state): self    {        return $this->with('inventoryState', $state);    }    public function withHeroActions(array $actions): self    {        return $this->with('heroActions', $actions);    }}

Semitexa SSR Axioms

One model should survive first paint, late HTML, live refresh, and interaction without a framework handoff.

If a rendering approach collapses the moment the page becomes dynamic, it was never a complete rendering model. Semitexa SSR is opinionated precisely because it tries to keep that model intact instead of outsourcing interaction to a second UI runtime.

The page response must be explicit before template rendering begins.

Templates are presentation surfaces, not a place to fetch from databases, hit APIs, or smuggle handler logic into Twig.

A region is a resource with a pipeline, not a partial with lucky context.

Deferred does not mean “replace SSR with client rendering later”. It means late server HTML.

Reactive does not mean “invent a mini SPA for one widget”. It means the server keeps re-rendering truth.

Interactivity does not require escaping into bespoke API glue. Components can declare backend event contracts directly.

JavaScript may enhance the page, but Semitexa refuses to require React, Alpine, Angular, or another client framework as the second rendering layer.

Assets, scripts, and SEO stay inside the same rendering system instead of becoming a parallel deployment concern.

Primitive Why it exists
HtmlResponse Page-level response object accumulates the render contract before Twig.
Resource DTOs Handlers shape presentation data before it reaches the template, which blocks template-side data drift at the architectural boundary.
Slot Resources Page regions use the same response discipline as the page itself.
Deferred Blocks Slow regions arrive later as HTML over SSE, not as client-owned redraw logic.
Reactive Slots Live UI keeps re-rendering server truth instead of mirroring it in a frontend state graph.
Component Event Bridge Interactive components can trigger backend events without abandoning the SSR component contract.
Component Script Assets Small component-owned JavaScript is allowed, but only as scoped enhancement rather than as a framework takeover.

How it works

Resource DTOs define the page boundary before Twig renders a single field, slot resources give regions their own response pipeline, deferred blocks stream later HTML instead of triggering client takeover, reactive slots keep re-rendering server truth, and interactive components stay scoped through tiny component-owned JavaScript instead of a page-level UI framework dependency.

Why it matters

Most stacks keep the phrase SSR but abandon the idea the moment the page becomes dynamic. Templates start reshaping data, partials start guessing context, and eventually view files begin pulling from storage or external APIs directly. Semitexa treats that drift as a framework problem, not a code review suggestion. It is opinionated because it is trying to preserve one rendering story, one presentation boundary, and one source of truth even when the page becomes late, live, or interactive.

Key concepts

one rendering story
The same conceptual model survives first paint, deferred delivery, live refresh, and component interaction.
server-owned truth
The browser displays HTML, but the authoritative rendering contract and state interpretation stay on the server side.
presentation boundary
Templates consume already-shaped response data instead of fetching, reshaping, or interpreting domain data on their own.
late HTML
Deferred content arrives after the shell, but it still arrives as server-rendered HTML rather than a client-side redraw.
reactive SSR
A live region can keep refreshing from server truth while remaining part of the SSR page model.
interactive SSR component
A server-rendered component can declare backend event contracts without turning into a mini SPA island.
framework-free enhancement
Client behavior may exist, but it remains small, scoped, and component-owned instead of becoming a second UI framework layer.

What We Reject

Semitexa SSR is explicitly designed against rendering drift and framework dependency drift.

These are not random implementation details. They are the specific splits Semitexa tries to prevent before they turn an SSR app into a pile of “just one more client-side exception” and “just one more required frontend library”.

Common drift Semitexa answer
“SSR for the first paint, client app for everything real.” Semitexa keeps deferred and reactive regions inside the same HTML pipeline.
“The template can just query what it needs, call an API, or reshape the data inline.” Semitexa treats that as presentation-boundary failure. Handlers and resource DTOs must shape data before Twig sees it.
“This region is just a partial, pass whatever data it seems to need.” Semitexa promotes regions to slot resources with an explicit render contract.
“The widget is live, so we need a second state architecture.” Semitexa refreshes the slot from server truth and swaps HTML in place.
“This interaction needs fetch glue, custom endpoints, and manual wiring.” Semitexa components can now declare backend event contracts and dispatch through one generic bridge.
“Once the page becomes interactive, we obviously need React, Alpine, or another client UI framework.” Semitexa rejects that as a default assumption. Small component-owned scripts are enough when the server still owns rendering truth.
“SEO, assets, scripts, templates, and live behavior all belong to different systems.” Semitexa treats them as one rendering surface owned by one framework model.

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

Donate via PayPal