There are three kinds of user waiting for your application to tell them something: the one looking at the page, the one whose corporate proxy will not pass a WebSocket, and the one who closed the browser two hours ago. sgcWebSockets 2026.10 covers all three.
The Proxy That Will Not Pass a WebSocket
Server sent events are plain HTTP: a GET that stays open and a stream of text. Nothing in the path can object to it. The sgcHTML engine can now carry its updates that way instead of over a WebSocket, and the page can be told to decide for itself:
FEngine.SSE.Enabled := True;
FEngine.SSE.Endpoint := '/events';
FEngine.SSE.PostEndpoint := '/events/send';
FEngine.SSE.ReplaySize := 100;
FTemplate.Transport := htAuto; { htWebSocket, htSSE or htAuto }
With htAuto the page opens a WebSocket and falls back to the event stream on its own when it cannot. The replay buffer means a reader who was disconnected for a moment gets the updates missed while away, rather than a hole.
The part worth underlining: this works with the plain HTTP server. Pushing updates to a page no longer requires the WebSocket server at all.
The User Who Closed the Browser
Web Push reaches a browser that is not on your page, and one that is not running. The page subscribes, your application stores the subscription wherever it keeps its data, and one call sends the notification:
uses
sgcHTML_WebPush;
FWebPush := TsgcHTMLWebPush.Create(Self);
FWebPush.Enabled := True;
FWebPush.VAPIDPublicKey := ReadSetting('vapid.public');
FWebPush.VAPIDPrivateKey := ReadSetting('vapid.private');
FWebPush.Subject := 'mailto:info@example.com';
FWebPush.OnSaveSubscription := DoSaveSubscription;
FWebPush.OnLoadSubscriptions := DoLoadSubscriptions;
FWebPush.OnDeleteSubscription := DoDeleteSubscription;
FEngine.WebPush := FWebPush;
FWebPush.Send('user-42', 'Order 1042 shipped',
'It left the warehouse this morning.', '/orders/1042');
The library owns the protocol: the encrypted payload, the VAPID signature, the TTL and the urgency header. Your application owns the storage, through three events, because where subscriptions live is a decision only you can make. A subscription the push service has retired comes back through OnSubscriptionExpired so you can drop it.
The Component That Decides Between Them
Having two channels is only useful if something chooses. The notification component now does: a user who is looking at the page gets the notification in the page, and a user who is not gets a web push, when the application says that is allowed. One notification, one call, and the user is not told twice.
And the Two Components Around It
A notification inbox. A bell with an unread badge and the notifications of one user, with read and clear, so a notification missed is not a notification lost.
A notification preferences table. Where a human ticks how each kind of notification reaches them. It is the part everybody postpones, and the part that keeps you out of trouble with people who did not ask to be pushed.
Both work on every edition, over WebSocket or over server sent events.
Editions
Server sent events, the inbox and the preferences table are part of sgcHTML. Web Push itself, the part that reaches a closed browser, needs SGC_WEBPUSH, which is the Enterprise and All-Access editions.
Upgrading
Everything here is off by default. An existing page keeps its WebSocket transport and renders the same markup until you set Transport, assign a WebPush or drop one of the two new components on a form.
Read Next
- Run Your Delphi App Inside ChatGPT and Claude
- Turn a Screenshot Into a Delphi Web Page
- sgcWebSockets 2026.10, everything else in this release
Watch It
There is a short video of this on the eSeGeCe channel.
Questions, feedback or migration help? Get in touch — you will get a reply from the people who wrote the code.
