A report says, “There are twelve delayed deliveries.” Beside the sentence, a live counter now says fourteen.
Which one is wrong?
Possibly neither. The sentence describes the state when the report was written. The counter describes a more recent observation. The problem is that the interface has hidden the difference.
This is the central design challenge of live islands in static reports. The subscription is the easy part. The harder part is giving changing data a clear relationship to a document that makes claims about a particular moment.
A document and a dashboard make different promises
A dashboard is usually interpreted as a view of current state. A report is usually interpreted as a coherent authored account.
Combining them can be useful: a stable incident review with current service status, a shipment summary with a live exception count, or a research note with a small panel of refreshed observations. But the live section must not silently rewrite the meaning of the static one.
I would keep authored prose tied to its evidence snapshot and place live values in explicit components. The document says when its analysis was produced. Each live component says when its data was observed and whether updates are healthy.
A timestamp belongs to a value, not merely to the page footer.
“Island” describes a boundary, not a freshness guarantee
Astro’s islands architecture describes selectively adding interactivity to otherwise static content.[1] That is a useful conceptual starting point, but a live data island needs additional contracts for identity, authorization, subscription ownership, and stale state.
The page might be server-rendered, statically generated, or stored as an artifact in another application. The essential boundary is the same: most of the document remains ordinary content while a small trusted component handles a bounded interactive concern.
This article uses “live island” in that application-design sense. It does not assume a particular frontend framework.
The model should select from approved component types through structured data. It should not invent arbitrary HTML attributes that the application then interprets as permission to subscribe to whatever resource they name.
Give the component a stable data contract
A minimal component request might look like this:
{
"kind": "live_metric",
"metric_ref": "metric-example-8",
"label": "Delayed deliveries",
"display": "integer",
"snapshot": {
"value": 12,
"observed_at": "2026-10-06T08:00:00Z"
}
}
The backend resolves metric_ref under the viewer’s identity. The model cannot turn a reference into a new subscription scope by changing the label or inserting an endpoint.
The trusted definition supplies units, permitted aggregation, freshness policy, and the authoritative source. The display component should not accept arbitrary formatting code from the model.
Keep a snapshot even when the component is live. It supports initial rendering, offline export, and an intelligible fallback when the live service is unavailable. An old snapshot must be labelled as old; it should not inherit a green “live” badge just because the page loaded successfully.
Share the transport, not the authorization
A document may show the same metric in a heading, a summary card, and a table. Opening three identical subscriptions wastes resources and complicates cleanup.
A parent-level subscription registry can deduplicate them. Its key includes the authorized scope, resource, query parameters, and any aggregation or version that changes the meaning of the result. Mounted components acquire a subscription; unmounted components release it. The transport closes when the reference count reaches zero, possibly after a short bounded grace period to avoid reconnect churn.
Do not deduplicate across users merely because a metric name matches. Reusing a transport or a cache must preserve the authorization boundary. A global registry that forgets tenant scope is a data-leak mechanism with a performance-oriented name.
The parent can batch requests and coalesce updates. Rendering every intermediate value is not always useful; maintaining a bounded cadence can keep a page responsive. Preserve events that matter to the component’s semantics, though. A latest-value counter and an audit log have different loss tolerances.
Order updates by the source’s version
Messages can arrive late or out of order. A client that accepts whichever update arrives last can move backward.
Use a server-defined monotonic sequence or a comparable resource version when the source provides one. Its ordering domain must be explicit: one resource, one stream generation, or one partition. A sequence that restarts after a reconnect needs a generation identifier or a fresh snapshot before incremental updates resume.
A local receipt timestamp is not a substitute for an authoritative version. Neither is a client-generated sequence that merely describes arrival order.
The component should have explicit states such as loading, current, stale, disconnected, and unavailable. “Disconnected” does not necessarily mean the last value is invalid. “Current” should mean the value satisfies a documented freshness condition, not simply that a socket exists.
When authorization is revoked, clear or replace the sensitive live state according to the product’s access policy. Continuing to display an old value may be unacceptable even when reconnecting is forbidden.
Reconnection needs a snapshot story
A websocket reconnecting successfully does not prove that the client saw every update while disconnected.
For a latest-value metric, fetching a fresh authorized snapshot may be enough. For a sequence of events, the server needs a supported cursor or replay mechanism, or the UI must declare the gap and reload the relevant view.
Keep the recovery path bounded. A component that reconnects indefinitely without backoff can turn a service outage into a retry storm. A registry should coordinate retries across its subscribers rather than letting every card run its own loop.
Also define what happens after tab suspension, browser sleep, document restoration, and a viewer switching accounts. Those transitions frequently break assumptions that look correct during a continuous development session.
An iframe is a separate application boundary
Some documents render inside isolated frames. In that design, the trusted parent should usually own authenticated data access and deliver only authorized, bounded updates through a narrow channel.
For known-origin frames, verify both the expected sender window and the exact origin. Validate every message against a schema and the current document instance. MDN documents the need to control target origins and inspect received message origins and sources.[2]
Sandboxed frames can have opaque origins, which changes what origin checks can establish.[3] A random channel identifier can help distinguish document instances, but it is not authorization. Keep the parent’s allowed message operations narrow, verify the sender window, and never let a child request arbitrary authenticated API calls.
If delivery requires a wildcard target because the child has an opaque origin, minimize the data crossing the boundary and bind delivery to the intended current frame. Consider whether a dedicated-origin renderer gives a simpler security model for the sensitivity of the data involved.
An isolated artifact should not be able to acquire powers by sending a convincing message to its parent. Treat its requests as untrusted input even when the model that generated it is usually helpful.
Export should freeze a meaning, not just a screenshot
A downloaded report should explain what its values represent after the connection disappears.
On export, resolve each live component to an authorized snapshot and include its observation time. Mark unavailable values honestly. Preserve the document’s authored timestamp separately from the export timestamp and the metric’s observation timestamp.
Those three moments can differ:
authored_at → when the analysis was written
observed_at → when this metric was measured
exported_at → when this copy was produced
Do not silently replace historical analytical inputs with current data while retaining the original conclusions. That produces an attractive document with an incoherent evidence trail.
For an interactive archive, offer an explicit choice between the published snapshot and current observations. The distinction should be visible before the reader interprets the numbers.
Real-time behavior needs accessible restraint
A rapidly changing interface can be distracting or difficult to use. Preserve focus, avoid layout shifts, and provide a pause or snapshot mode where continuous updates would interfere with reading.
Do not announce every tick to assistive technology. Decide which transitions deserve an announcement and which can remain available for inspection. A meaningful status change and a frequently updating count should not necessarily use the same announcement behavior.
Test with slow data, missing data, long labels, multiple identical components, revoked access, and a disconnected tab returning after several hours. These are normal conditions for a document that is supposed to outlive the session that created it.
The most important label is not “live”
It is “as of.”
Live components are valuable when they add a current view without damaging the integrity of the authored record. That requires more than a stream of numbers. It requires a contract about identity, freshness, failure, and what happens when the page stops being live.
Build that contract once in the trusted component layer. Then the model can request useful views without having to invent a real-time application inside every document it writes.
Technical notes
[1] Astro, Islands architecture. The subscription and document-semantics design above extends the general selective-interactivity idea; it is not a claim about Astro’s built-in data guarantees.
[2] MDN, Window.postMessage.
[3] MDN, The iframe element. Sandbox configuration and origin behavior need review in the actual embedding environment.
Continue reading
Model-Agnostic AI Artifacts: Give the Model a UI Contract, Not Your Frontend · Redis in Production: Stop Calling Everything a Cache