Cached sidebar head fragment
Server-rendered integrations can reserve space before the asynchronous sidebar loads by fetching GET /sidebar.js?format=head&id=<webshop-id> from the sidebar worker. The response is JSON:
{
"schemaVersion": 1,
"configRevision": "<sha256>",
"rendererRevision": "<sha256>",
"revision": "<sha256 of headHtml>",
"headHtml": "<style>...</style><script>...</script>"
}
Cache the response on the integration backend. Insert headHtml directly in the server-rendered document's <head>, before the normal asynchronous sidebar loader. The ES6 bootstrap lives in js/sidebar_worker/src/client/sidebar-placeholder.js and is minified at worker build time. Keep the generated script inline: it uses a MutationObserver on the document root to prepend a responsive placeholder as soon as the body appears, then disconnects. If the body already exists, insertion is immediate. Duplicate execution is safe.
The placeholder occupies the same body layout position as the finished top bar. It matches the existing body margin/padding offset without changing either the body's styles or root padding. On mount the runtime refreshes any cached placeholder CSS. The top bar removes the placeholder and its stylesheet in its layout effect, before paint, keeping its existing DOM, normal-flow panel and negative-margin positioning. While the mobile layout is still loading, the placeholder stays in place. Disabled layouts clear it immediately.
The fragment is optional: without it the sidebar renders normally, but cannot reserve space before its JavaScript loads. Any page-specific mobile override must run before the fragment. This is first-party executable markup, not escaped text. Fetching it from the visitor's browser after rendering would defeat the reservation.
The fragment is independent of the requesting device. Its media queries use the same geometry as the sidebar runtime, and its small bootstrap respects session dismissal. Cached settings only decide whether to insert a placeholder; the bootstrap never writes sidebar configuration globals. The runtime uses its own current settings and any explicit page overrides, removing a stale placeholder if the top bar is no longer configured. Missing/ineligible webshops and disabled mobile layouts do not reserve space. Keep the last successful fragment if a refresh fails. A failed sidebar script can leave an empty reserved strip.
The revisions have different purposes:
configRevisionhashes the ordered JSON object{enabled, mobile, system}, using the same eligibility and cleaned settings as sidebar data generation.rendererRevisionidentifies the head generator, shared geometry and build inputs. The worker build writes it tojs/sidebar_worker/dist/head-renderer-revision.revisionhashes the exact emitted markup, for consumers that deduplicate or verify cached content.
The endpoint deliberately returns Cache-Control: no-store; integrations own persistent caching. Workers KV propagation can still briefly serve older data.
Shopify notifications
After sidebar KV updates/deletions, Symfony Messenger delivers a notification to Shopify for every updated webshop, containing the current configuration and renderer revisions. The dashboard’s Shopify signup columns do not identify all connections; Shopify matches webshop IDs against its own connections and returns HTTP 200 for unmatched IDs. Deletions are notified too. Worker deployment also queues notifications for all webshops, so a code change invalidates cached HTML even without merchant edits. Successful identical notifications are deduplicated for a day.
Shopify fetches the head fragment and validates those expected revisions before publishing its metafield. HTTP 409 means the worker has not caught up; Messenger retries after one minute and recomputes the expected revisions each attempt. Other HTTP failures use the existing retry policy. No browser reporting is used.
Ship dist/head-renderer-revision alongside dist/index.mjs in the dashboard release. Deploy the worker and dashboard notifier together, then the companion Shopify backend/theme extension. Early webhook failures are retryable. Existing Shopify installs can be backfilled with their app's web/lib/update_app_metafields.php.
Validation
nix develop -c make -C js/sidebar_worker test
nix run .#sidebar-visual -- sidebar.head.spec.js sidebar.loading.spec.js
make test
The browser suite inserts the actual worker fragment before the body, delays the sidebar response until after paint, and measures layout shifts and content position through loading, popup preload, rotation and dismissal, including the exact viewport boundaries. It also checks duplicate execution, an existing body, missing/stale styles and theme root padding/body margins. Hidden popup preloading keeps its existing panel dimensions until opened, avoiding an unrelated margin animation during loading.