Real-Time Updates
CWA is live by default. When an admin publishes a component, every open browser session receives the update via the Mercure hub — no page reload, no polling, no WebSocket code on your end.
How It Works
- An admin publishes a component via the CMS
- The Symfony API sends an update to the Mercure hub (because
mercure: trueis on the entity) - The hub broadcasts the update to all subscribed browser connections
- The Nuxt module's resource store receives the event and updates the relevant IRI
- Vue's reactivity re-renders any component bound to that resource
- A non-intrusive notification toasts for regular visitors ("Content updated")
You write none of this plumbing. It's in the module.
Prerequisites
PHP Side
Add mercure: true to the #[ApiResource] attribute on any entity you want to broadcast:
#[ApiResource(mercure: true)]
class Title extends AbstractComponent { ... }
Without this, the resource updates silently (the API saves the change, but no Mercure event fires).
Infrastructure
You need a Mercure hub running alongside your API. The Docker Compose template includes one via dunglas/mercure.
Required environment variables:
# API → hub (internal, server-side)
MERCURE_URL=http://mercure/.well-known/mercure
# Browser → hub (public-facing)
MERCURE_PUBLIC_URL=https://mercure.example.com/.well-known/mercure
# Shared secret for publisher JWT (keep private)
MERCURE_JWT_SECRET=your_secure_secret
MERCURE_URL and MERCURE_PUBLIC_URL can be the same in simple deployments (e.g. a single server where your API and hub are on the same host). They differ in Docker Compose setups where the API uses an internal hostname.
No Front-End Code Needed
The module subscribes to the Mercure hub in its Nuxt plugin. Topics are derived automatically from the IRIs of resources that have been fetched for the current page. You never write EventSource or WebSocket code.
The subscription lifecycle:
- On page load: the module opens an
EventSourceconnection toMERCURE_PUBLIC_URL - Topics: all IRIs currently in
cwa.resources.currentIds - On navigation: the subscription updates to the new page's resource IRIs
- On sign-in: Mercure re-initialises to include private topics (admin resources)
The Visitor Notification
When a Mercure update arrives for a non-admin visitor, a toast notification appears: "Content on this page has been updated." The visitor can dismiss it. Whether they dismiss it or not, the resource store has already updated — new content is in the DOM.
Admins editing in real time see their changes immediately without any notification.
Troubleshooting
No live updates arriving in the browser:
- Check that
MERCURE_PUBLIC_URLis reachable from browsers — not an internal Docker hostname - Confirm
mercure: trueis on the PHP entity - Open DevTools → Network → Filter by
EventSource— you should see a persistent connection to the hub - Check the hub logs for authentication errors
Hub 401 errors:
- The
MERCURE_JWT_SECRETmust match exactly between the API (publisher) and the hub (subscriber) - Verify the secret is identically set in both services
Updates arrive but the component doesn't re-render:
- Confirm the component uses
useCwaResourceoruseCwaImageResource— both react to store updates - Bare
$fetchcalls are not reactive; usecwa.resources.getResource(iri)for reactive lookups
Advanced: Custom Topics
By default CWA subscribes only to the exact IRIs of resources fetched for the current page. The module does not subscribe to wildcard or collection topics automatically.
To subscribe to additional topics (e.g. a {+https://example.com/component/products} wildcard that covers all products), you need two things:
- Hub side — your Mercure hub must be configured to allow that topic pattern for subscriber tokens.
- Module side — open a second
EventSourcein a Nuxt plugin or composable that passes the extra topics in thetopicquery parameter.
There is no built-in CWA API for registering custom topics today. This is an advanced use case best solved by adding a Nuxt plugin that opens a secondary EventSource connection alongside the one the module manages. The module's resource store is reactive — once a resource IRI updates in the store (via $cwa.resources.setResource(iri, data)) any component bound to that IRI will re-render, regardless of which EventSource delivered the update.