Problem
The UVE editor silently accepts the legacy, beta-era params-only CLIENT_READY shape — initUVE({ params: { depth: 3 } }) — and leaves the headless client in a half-connected state:
- The client is marked ready (
setIsClientReady(true)), params are honored on the editor's page fetch (depth appears on the iframe URL)
- Non-gated events (
uve-request-bounds / uve-flush-bounds / uve-scroll-inside-iframe / selection) flow normally
- But
uve-set-page-data / uve-reload-page are never emitted on contentlet add/remove/save, so CONTENT_CHANGES / PAGE_RELOAD subscriptions never fire and the canvas never re-renders until a manual browser refresh
There is no warning, no error, and no documentation stating that registering a GraphQL query via CLIENT_READY is required for content-event delivery. The runtime accepts a shape the SDK types already reject (DotCMSPageResponse has no params field), and the degradation is completely silent.
Why this matters: every integration built on the 0.0.1-beta.x line that migrates to 26.7.x and keeps its hand-wired bridge (initUVE({params}) + manual createUVESubscription) will hit exactly this half-working state. Diagnosing it requires instrumenting the postMessage channel and reading editor source — this cost a real integration team days of instrumentation and a multi-day support investigation.
Code path (verified on main)
- Gate:
edit-ema-editor.component.ts → $handleReloadContentEffect — if (pageType === PageType.HEADLESS && !hasClientQuery) return; (deliberate: "Never push a REST-sourced pageAsset… skip until a GraphQL-backed update is available")
requestMetadata is only set from CLIENT_READY when devConfig.graphql.query is present: dot-uve-actions-handler.service.ts → CLIENT_READY handler (setCustomClient only if (query))
params-only configs still get pageReload(pageParams) + setIsClientReady(true) → the half-connected state, with no feedback
uve-reload-page only fires on narrow paths (e.g. SAVE_PAGE with clientAction === EDIT_CONTENTLET), so add/remove emits nothing to these clients
- SDK type contract (
DotCMSPageResponse, sdk/types/.../public.ts) doesn't include params — the runtime accepts a shape the types reject
Repro
- Headless site in UVE; the app initializes with
initUVE({ params: { depth: 3 } }) and subscribes to UVEEventType.CONTENT_CHANGES.
- In the editor, add or remove a contentlet.
- Observe (raw
message listener): bounds/scroll/selection events arrive; uve-set-page-data / uve-reload-page never do. Canvas doesn't update until manual refresh. No console warning anywhere.
Verified against @dotcms/uve / @dotcms/types 26.7.21-1 with a 26.07.06-3 backend, with a custom rendering layer (no @dotcms/react/@dotcms/angular).
Expected
- Warn loudly when a headless client sends
CLIENT_READY without graphql.query — editor console (and ideally an editor UI hint): "client registered without a page query; content-change events will not be delivered. Pass the full client.page.get() response to initUVE()."
- Document and formally deprecate the params-only shape: state in the
@dotcms/uve docs/migration guide that initUVE() expects the full DotCMSPageResponse (whose graphql field registers the client for CONTENT_CHANGES/PAGE_RELOAD delivery), and that the beta-era { params } shape is legacy/unsupported.
(Optional, for triage discussion: a lightweight invalidation signal for query-less clients was considered, but since the supported path — passing the full client.page.get() response — registers the query automatically, warning + documentation likely covers the real-world cases.)
Workaround / correct usage
Fetch via @dotcms/client's client.page.get() and pass its entire response to initUVE(pageResponse) — the response carries the graphql metadata, so registration happens automatically. Framework packages (useEditableDotCMSPage in React/Vue, DotCMSEditablePageService in Angular) do all of this internally, as shown in the official examples (examples/nextjs, examples/angular).
Links
Problem
The UVE editor silently accepts the legacy, beta-era params-only
CLIENT_READYshape —initUVE({ params: { depth: 3 } })— and leaves the headless client in a half-connected state:setIsClientReady(true)),paramsare honored on the editor's page fetch (depthappears on the iframe URL)uve-request-bounds/uve-flush-bounds/uve-scroll-inside-iframe/ selection) flow normallyuve-set-page-data/uve-reload-pageare never emitted on contentlet add/remove/save, soCONTENT_CHANGES/PAGE_RELOADsubscriptions never fire and the canvas never re-renders until a manual browser refreshThere is no warning, no error, and no documentation stating that registering a GraphQL query via
CLIENT_READYis required for content-event delivery. The runtime accepts a shape the SDK types already reject (DotCMSPageResponsehas noparamsfield), and the degradation is completely silent.Why this matters: every integration built on the
0.0.1-beta.xline that migrates to26.7.xand keeps its hand-wired bridge (initUVE({params})+ manualcreateUVESubscription) will hit exactly this half-working state. Diagnosing it requires instrumenting the postMessage channel and reading editor source — this cost a real integration team days of instrumentation and a multi-day support investigation.Code path (verified on main)
edit-ema-editor.component.ts→$handleReloadContentEffect—if (pageType === PageType.HEADLESS && !hasClientQuery) return;(deliberate: "Never push a REST-sourced pageAsset… skip until a GraphQL-backed update is available")requestMetadatais only set fromCLIENT_READYwhendevConfig.graphql.queryis present:dot-uve-actions-handler.service.ts→CLIENT_READYhandler (setCustomClientonlyif (query))params-only configs still getpageReload(pageParams)+setIsClientReady(true)→ the half-connected state, with no feedbackuve-reload-pageonly fires on narrow paths (e.g.SAVE_PAGEwithclientAction === EDIT_CONTENTLET), so add/remove emits nothing to these clientsDotCMSPageResponse,sdk/types/.../public.ts) doesn't includeparams— the runtime accepts a shape the types rejectRepro
initUVE({ params: { depth: 3 } })and subscribes toUVEEventType.CONTENT_CHANGES.messagelistener): bounds/scroll/selection events arrive;uve-set-page-data/uve-reload-pagenever do. Canvas doesn't update until manual refresh. No console warning anywhere.Verified against
@dotcms/uve/@dotcms/types26.7.21-1 with a 26.07.06-3 backend, with a custom rendering layer (no@dotcms/react/@dotcms/angular).Expected
CLIENT_READYwithoutgraphql.query— editor console (and ideally an editor UI hint): "client registered without a page query; content-change events will not be delivered. Pass the fullclient.page.get()response toinitUVE()."@dotcms/uvedocs/migration guide thatinitUVE()expects the fullDotCMSPageResponse(whosegraphqlfield registers the client forCONTENT_CHANGES/PAGE_RELOADdelivery), and that the beta-era{ params }shape is legacy/unsupported.(Optional, for triage discussion: a lightweight invalidation signal for query-less clients was considered, but since the supported path — passing the full
client.page.get()response — registers the query automatically, warning + documentation likely covers the real-world cases.)Workaround / correct usage
Fetch via
@dotcms/client'sclient.page.get()and pass its entire response toinitUVE(pageResponse)— the response carries thegraphqlmetadata, so registration happens automatically. Framework packages (useEditableDotCMSPagein React/Vue,DotCMSEditablePageServicein Angular) do all of this internally, as shown in the official examples (examples/nextjs,examples/angular).Links