← UI Rendering & SSR

UI RENDERING & SSR FEATURE

Component Script Assets

No more “remember to include the JS somewhere on this page”. If a component needs optional client enhancement, the contract lives on the component itself.

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 Semitexa SSR component can declare its own optional enhancement asset, so the client behavior belongs to the component contract instead of being manually remembered by the page.

How it works

Add script to #[AsComponent], let ComponentRenderer auto-require the runtime and the declared asset when that component actually renders, and register the client behavior through SemitexaComponent.register() so the runtime can mount each rendered root.

Why it matters

This closes one of the oldest sources of frontend drift: the HTML lives in the component, but the behavior is hidden in page-level includes and boot glue. Semitexa keeps markup and optional enhancement under the same component ownership model.

Key concepts

script
Optional AsComponent field that declares the canonical enhancement asset key.
auto-require
The renderer pulls in the runtime and the component asset only when the component is present on the page.
SemitexaComponent.register()
Frontend runtime API that registers one mount function for a named SSR component.
component root
The rendered root element annotated so the runtime can mount behavior per instance.

Component Script Assets

The SSR component owns its optional enhancement asset.

These cards are plain SSR markup on first paint. Their behavior appears only because the component contract itself declares a script asset, and the runtime mounts that script per rendered root.

Shell

Server HTML first

The initial markup is already meaningful before any client enhancement runs.

Asset boots SSR only
Mount order Waiting
Interactions 0

Server rendered shell. Waiting for optional client enhancement…

Ownership

Behavior travels

The page did not manually remember this asset. The component contract did.

Asset boots SSR only
Mount order Waiting
Interactions 0

Server rendered shell. Waiting for optional client enhancement…

Scale

One asset, many roots

The same script loads once, then mounts each rendered component instance independently.

Asset boots SSR only
Mount order Waiting
Interactions 0

Server rendered shell. Waiting for optional client enhancement…

script: Declared on #[AsComponent]
1x Asset load per page
n Root mounts per rendered instance
#[AsComponent] script SemitexaComponent.register() auto-require SSR component root

Verified against Semitexa Ultimate 2026.09.19.1020

Component Script Assets

No more "remember to include the JS somewhere on this page". If a component needs optional client enhancement, the contract lives on the component itself. A Semitexa SSR component can own its optional enhancement asset, so behavior travels with the component instead of leaking into page-level glue.

How it works

The script key in #[AsComponent] declares the canonical asset that enhances this component. The component renderer auto-requires that script only when the component actually appears on the page. On the client side, SemitexaComponent.register() mounts the behavior per component root, so one script safely enhances many instances.

The rules

  • The script is declared on the component contract, not remembered manually by the page.
  • The asset loads only if that component actually rendered on the page.
  • The runtime mounts behavior per component root, so one script can safely enhance many instances.
  • The script remains optional enhancement, not a second rendering authority.
  • The component keeps owning its surface: Twig for HTML, optional script for progressive enhancement.

Key mechanisms

  • script — canonical asset key declared directly in #[AsComponent].
  • auto-require — ComponentRenderer requires the runtime and the asset only when the component appears.
  • SemitexaComponent.register() — client behavior registers once and mounts each rendered component root independently.
  • No page glue — feature pages stop manually remembering which component enhancement script to include.

Why this matters

Page-level script includes create implicit dependencies between templates and JavaScript files. When a component moves to a different page, the include has to move too. Declaring the script on the component class makes the dependency explicit and co-located, so the component can render anywhere without extra wiring.

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

Component Contract Implementation slice
<?phpdeclare(strict_types=1);use Semitexa\Ssr\Attribute\AsComponent;#[AsComponent(    name: 'product-spotlight-card',    template: '@shop/components/product-spotlight-card.html.twig',    script: 'shop:js:product-spotlight-card',)]final class ProductSpotlightCardComponent{    public function __construct(        public readonly string $title,        public readonly string $summary,    ) {}}

Enhancement Contract

Optional JavaScript belongs to the component, not to page memory.

What this fixes

  • The script is declared on the component contract, not remembered manually by the page.
  • The asset loads only if that component actually rendered on the page.
  • The runtime mounts behavior per component root, so one script can safely enhance many instances.
  • The script remains optional enhancement, not a second rendering authority.
  • The component keeps owning its surface: Twig for HTML, optional script for progressive enhancement.

What the framework guarantees

  • script: Canonical asset key declared directly in #[AsComponent].
  • auto-require: ComponentRenderer requires the runtime and the asset only when the component appears.
  • SemitexaComponent.register(): Client behavior registers once and mounts each rendered component root independently.
  • No page glue: Feature pages stop manually remembering which component enhancement script to include.

How it works

Add script to #[AsComponent], let ComponentRenderer auto-require the runtime and the declared asset when that component actually renders, and register the client behavior through SemitexaComponent.register() so the runtime can mount each rendered root.

Why it matters

This closes one of the oldest sources of frontend drift: the HTML lives in the component, but the behavior is hidden in page-level includes and boot glue. Semitexa keeps markup and optional enhancement under the same component ownership model.

Key concepts

script
Optional AsComponent field that declares the canonical enhancement asset key.
auto-require
The renderer pulls in the runtime and the component asset only when the component is present on the page.
SemitexaComponent.register()
Frontend runtime API that registers one mount function for a named SSR component.
component root
The rendered root element annotated so the runtime can mount behavior per instance.

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

Donate via PayPal