Real-time web / Engineering guide
Streaming SSE with Semitexa: Live PHP Updates and HTML
A page is ready, but its chart is still loading. A background job advances, but the browser has no reason to ask again. Streaming SSE lets the server send each update as it becomes ready over one open HTTP response.
What is streaming SSE?
Streaming SSE means keeping an HTTP response open and sending a sequence of Server-Sent Events instead of waiting to return one complete response. The browser reads the text/event-stream response with EventSource. Each event ends with a blank line; when that frame arrives, JavaScript can act on it while the connection stays open.
Consider a dashboard with a fast heading and a slow chart. A conventional request either makes the whole page wait or asks the browser to fetch the chart separately. A streaming response can deliver the initial page now and the chart when it is rendered. Later events can carry fresh status without a timer that repeatedly polls the server.
event: progress
data: {"job":"report-42","percent":40}
event: progress
data: {"job":"report-42","percent":80}
event: complete
data: {"job":"report-42","url":"/reports/42"}
Those frames illustrate the standard SSE wire format. SSE is the delivery mechanism, not a job queue or a database of missed messages. Your application still decides what to send, who can receive it, and how to recover after a disconnect. If you need the protocol basics first, read our Server-Sent Events guide.
How streaming SSE works in Semitexa
Semitexa has two complementary ways to see the idea in action. The SSE stream demo opens an EventSource connection and shows named events generated by the backend. The deferred blocks demo starts with server-rendered HTML and streams completed regions into their placeholders. Both use the PHP/Swoole runtime; the browser is observing actual server output.
- Render a useful page first. The deferred view sends the page shell and skeleton regions without waiting for every slot to finish.
- Render slow regions on the server. A slot handler supplies data and a Twig template produces the final HTML. The browser does not have to rebuild that region from JSON.
- Deliver results over SSE. The deferred runtime opens its
/__semitexa_kissstream, receives completed blocks, and replaces their placeholders. When the page also has live UI events, the runtime can use the same session and channel instead of opening a separate stream for each feature.
That is the useful Semitexa difference for a server-rendered product: streaming is part of the page delivery model. You can keep your view logic in PHP and Twig, while the transport moves finished HTML to the page as soon as each region is ready. The rendering demos show this alongside other SSR features.
Two distinct use cases: use named SSE events when the browser needs to interpret data such as job progress. Use deferred HTML delivery when the server already owns the UI region. Semitexa supports both; the right choice follows what the client needs to do with an update.
A real Semitexa deferred slot
The chart in Semitexa Demo is declared as a typed slot resource. Here is the relevant part of its actual declaration:
#[AsSlotResource(
handle: 'demo_deferred_blocks',
slot: 'deferred_chart_widget',
template: '@project-layouts-semitexa-demo/deferred/chart-widget.html.twig',
deferred: true,
skeletonTemplate: '@project-layouts-semitexa-demo/deferred/chart-widget.skeleton.html.twig',
clientModules: ['@project-static-semitexa-demo/deferred/chart-widget.js'],
)]
final class DeferredChartWidgetSlot extends HtmlSlotResponse
{
// The slot handler supplies chart data to this resource.
}
The declaration connects a page handle and slot name to its final Twig template and a skeleton. The page template must place the deferred slot, and its handler must populate the resource. Once wired, the runtime handles late delivery over SSE. clientModules adds behavior for this chart after insertion; it is not required just to receive the HTML.
This matters when a page has several independent slow regions. A product list can be ready while analytics is still computing. Users see the content that is ready, and each later block arrives in the same page instead of forcing an all-or-nothing render. The deferred blocks example lets you inspect the resulting page and shared stream.
Try streaming SSE in the live Semitexa demos
Watch a backend event stream
Open the SSE stream demo, sign in, and connect. The client displays a connection event and listens for named messages such as notification and scheduler.tick. In a debug-enabled demo environment, its countdown follows the next minute boundary and the tick comes from a server-side producer, not the countdown timer. The showcase producer is deliberately disabled when APP_DEBUG is off; ordinary application streams do not depend on it. The page exposes the handler and client JavaScript beside the preview.
Watch server-rendered HTML arrive
Open the deferred blocks demo to see the page shell and skeletons before completed regions arrive. Its stream observer reports the lifecycle of the shared /__semitexa_kiss connection. The runtime owns that connection; the observer does not open another one. A sign-in is required for the persistent stream shown in this demo.
The distinction is visible in the browser: one demo shows event data, and the other shows finished HTML replacing a placeholder. Together they show why SSE streaming is useful beyond a console that prints messages.
What to plan before using streaming SSE in production
- Keep frames flowing. Proxy buffering and compression can delay small events. Check the complete path from Swoole to the browser. NGINX documents proxy buffering and the
X-Accel-Bufferingresponse header. - Control connection cost. A stream stays open, so budget for concurrent viewers, idle timeouts, heartbeats, and disconnect cleanup. The MDN SSE guide also explains browser connection limits, especially over HTTP/1.x.
- Authorize the data. A live stream can outlast the request that opened the page. Check who may subscribe and what each event may reveal. Semitexa's persistent demo stream asks for authentication before it opens.
- Design recovery.
EventSourcenormally reconnects after interruption. Reconnection alone does not replay missed updates. Decide whether a view can refresh its current state or needs event IDs, retention, and replay.
Measure the real delivery path. A frame that leaves PHP immediately but sits in a proxy buffer is not a live update for your user.
Streaming SSE FAQ
Is streaming SSE different from Server-Sent Events?
No. It emphasizes the way an SSE response stays open and delivers multiple events over time. Each completed event can update the browser before the response ends.
Does Semitexa require a single-page application for streaming?
No. Deferred slots start from a server-rendered page and deliver completed Twig HTML into it. Client JavaScript handles transport and insertion, while the server continues to own the region's markup.
Can the browser send messages back through the SSE connection?
No. SSE sends data from server to browser. Use a normal HTTP request to start or change work, then use the stream to receive progress or refreshed content.
Will EventSource recover every missed update?
No. It reconnects, but durable delivery needs application-level state, replay, or a fresh snapshot after reconnecting.