Symptom
With an app host running Next 16 (next dev), Studio loads and code components register (insert menu lists them), but the canvas is unusable:
- clicks/hovers/inserts on artboards silently no-op
- outline is empty, even for a brand-new page
- right panel never opens
- console:
PromiseTimeoutError: Couldn't get userBody
Next ≤15 app hosts (e.g. examples on 15.5.9) are unaffected. Reproduced 2026-07-09 with a fresh create-plasmic-app scaffold (Next 16.2.9) against euwest Studio.
Root cause
Studio artboards are built by fetching the host page and document.write-ing it into a sourceless iframe (StudioCtx.hostPageHtml → CanvasFrame).
Next 16.2 enables a dev-mode React debug channel by default (experimental.reactDebugChannel: true in next/dist/server/config-shared.js). Its client (next/dist/client/dev/debug-channel.js) treats performance.getEntriesByType('navigation')[0].transferSize === 0 as "page restored from HTTP cache". A doc.write'n document always has transferSize === 0; the sessionStorage restore fails (the HTML came from Studio's fetch), and the client calls location.reload() as its fallback.
That reload destroys the artboard document seconds after Studio writes it. CanvasCtx keeps handles to the destroyed document, waitForUserBody times out, and the frame is never interactive. Bundler-independent (Turbopack and webpack both affected).
Minimal repro without Studio: fetch any dev-served page and document.write it into an iframe — the document self-destructs to about:blank within seconds; with the flag off it survives.
Workaround (consumer side)
// next.config.ts of the app host
experimental: { reactDebugChannel: false }
Verified: canvas overlay attaches ~6s after Studio reload; outline/selection/inserts work.
Possible actions
- Docs: app-hosting docs and
create-plasmic-app output should call this out (fresh scaffolds get Next 16, so every new app host hits it). Alternatively create-plasmic-app could write the flag into the generated next.config.ts.
- Studio hardening:
CanvasCtx could detect the artboard document being replaced (iframe load re-fires) and re-attach instead of holding stale handles — this would also cover other dev clients that hard-reload.
- Upstream: the
transferSize === 0 heuristic false-positives on any doc.write'n page; consider filing against Next.js.
Symptom
With an app host running Next 16 (
next dev), Studio loads and code components register (insert menu lists them), but the canvas is unusable:PromiseTimeoutError: Couldn't get userBodyNext ≤15 app hosts (e.g. examples on 15.5.9) are unaffected. Reproduced 2026-07-09 with a fresh
create-plasmic-appscaffold (Next 16.2.9) against euwest Studio.Root cause
Studio artboards are built by fetching the host page and
document.write-ing it into a sourceless iframe (StudioCtx.hostPageHtml→CanvasFrame).Next 16.2 enables a dev-mode React debug channel by default (
experimental.reactDebugChannel: trueinnext/dist/server/config-shared.js). Its client (next/dist/client/dev/debug-channel.js) treatsperformance.getEntriesByType('navigation')[0].transferSize === 0as "page restored from HTTP cache". A doc.write'n document always hastransferSize === 0; the sessionStorage restore fails (the HTML came from Studio's fetch), and the client callslocation.reload()as its fallback.That reload destroys the artboard document seconds after Studio writes it.
CanvasCtxkeeps handles to the destroyed document,waitForUserBodytimes out, and the frame is never interactive. Bundler-independent (Turbopack and webpack both affected).Minimal repro without Studio: fetch any dev-served page and
document.writeit into an iframe — the document self-destructs toabout:blankwithin seconds; with the flag off it survives.Workaround (consumer side)
Verified: canvas overlay attaches ~6s after Studio reload; outline/selection/inserts work.
Possible actions
create-plasmic-appoutput should call this out (fresh scaffolds get Next 16, so every new app host hits it). Alternativelycreate-plasmic-appcould write the flag into the generatednext.config.ts.CanvasCtxcould detect the artboard document being replaced (iframeloadre-fires) and re-attach instead of holding stale handles — this would also cover other dev clients that hard-reload.transferSize === 0heuristic false-positives on any doc.write'n page; consider filing against Next.js.