Skip to content

Bug Report: Page Builder Live Preview Fails After Multiple Edits #1768

@OignonFugace

Description

@OignonFugace

Disclaimer: This bug report was produced after an extended debugging session assisted by Claude (Anthropic's AI). Given the depth of context accumulated throughout the investigation, Claude was able to synthesize these findings and identify the patterns described above. I personally lack the expertise to confirm with certainty whether this is a genuine bug on Prismic's side or a misuse of the tool on my part. Having exhausted all the leads I could think of, I'm turning to the community for guidance. Any feedback — including "you're doing it wrong" — is genuinely welcome.


Bug Report: Page Builder Live Preview Fails After Multiple Edits

Summary

The Prismic Page Builder live preview fails after a series of content modifications. The issue manifests as an unexpected iframe refresh followed by a React hydration error.

Important discovery: The bug occurs on BOTH localhost AND Vercel deployments, but it takes significantly longer to appear on localhost (many more edits required). On Vercel, it happens much faster (after just a few edits).

CRITICAL: This bug has been reproduced on a minimal Next.js + Prismic project with:

  • No "use client" components
  • No animations (Framer Motion, etc.)
  • No complex slices
  • Just the basic Prismic starter template deployed to Vercel

Environment

  • Framework: Next.js 16.1.6 (App Router with Turbopack)
  • Prismic packages:
    • @prismicio/next: 2.2.1
    • @prismicio/react: 3.4.0
    • @slicemachine/adapter-next: 0.3.95
  • Hosting: Vercel (both staging and production environments tested)
  • Browser: Chrome (latest), tested in incognito mode as well

Steps to Reproduce

  1. Deploy a Next.js App Router project with Prismic to Vercel
  2. Open Prismic Page Builder
  3. Select a slice to preview
  4. The slice renders correctly initially
  5. Make a content modification (e.g., change text)
  6. After a few modifications, the iframe "refreshes" (visible flash)
  7. The next modification after the refresh causes a React error
  8. Must exit and re-enter the Page Builder to recover

Expected Behavior

  • Live preview should update smoothly without iframe refresh
  • Content modifications should never cause React errors
  • Behavior should be identical between localhost and Vercel

Actual Behavior

  • On localhost: Bug eventually appears after many edits (takes longer to manifest)
  • On Vercel: Bug appears much faster, after just a few edits

This suggests the bug is time/usage-based and that Vercel's environment (latency, caching) accelerates its occurrence.

Error Details

Minified React error #418
https://reactjs.org/docs/error-decoder.html?invariant=418

This error indicates a hydration mismatch between server and client rendering.

Investigation Findings

1. The Refresh is the Root Cause

The problem is NOT primarily the hydration error - it's the unexpected iframe refresh that shouldn't happen. The hydration error is a consequence of this refresh.

2. Identified Problematic Code Pattern

In @slicemachine/adapter-next/src/simulator/react-server/SliceSimulator.tsx:

// This code runs during render, not in useEffect
const state =
    typeof window !== "undefined"
        ? new URL(window.location.href).searchParams.get(STATE_PARAMS_KEY)
        : undefined;
const hasSlices = getSlices(state).length > 0;

The typeof window !== "undefined" check during render (not in useEffect) is a React anti-pattern that causes hydration mismatches. On the server, window is undefined, so state is undefined. On the client, state has a value.

3. router.refresh() Behavior Differs

The SliceSimulator uses router.refresh() to update the preview:

simulatorManager.state.on(StateEventType.Slices, (newSlices) => {
    // ... URL update logic
    setTimeout(() => router.refresh(), 0);
});

This router.refresh() behaves differently on Vercel vs localhost due to Next.js caching differences in production.

4. Localhost vs Vercel Differences

Aspect Localhost Vercel
router.refresh() Soft refresh Can cause full page refresh
React mode Development (verbose errors) Production (minified)
Caching Minimal Aggressive (Router Cache)
Hydration tolerance More lenient Strict

Attempted Fixes (All Failed)

  1. Stable React keys - Changed index-based keys to stable keys in all slices
  2. useMotionValue reset - Added reset mechanism in animation components
  3. Route Groups isolation - Isolated slice-simulator with its own layout
  4. VERCEL_FORCE_NO_BUILD_CACHE=1 - Disabled Vercel build cache
  5. Dynamic import with ssr: false - Loaded SliceZone only on client
  6. staleTimes configuration - Extended Router Cache duration
  7. revalidate = 0 + force-dynamic - Forced dynamic rendering
  8. CSP + X-Frame-Options headers - Configured iframe permissions

Hypothesis

Updated hypothesis based on the discovery that the bug also occurs on localhost (just slower):

The issue appears to be a state accumulation/degradation problem in the SliceSimulator or SimulatorManager:

  1. Event listener accumulation - The simulatorManager.state.on() listeners may be stacking up without proper cleanup
  2. Memory leak - State or references accumulating over time
  3. Corrupted singleton state - The SimulatorManager singleton may be getting into a bad state after repeated router.refresh() calls

The fact that Vercel accelerates the bug suggests that network latency or caching behavior triggers the problematic code path more frequently.

Original hypothesis (still relevant):

  • typeof window !== "undefined" check during render causes hydration mismatch
  • router.refresh() behavior differs between dev and prod

Requested Action

  1. Investigate event listener cleanup - Check if simulatorManager.state.on() listeners are properly removed on component unmount
  2. Review SimulatorManager singleton - Check for memory leaks or state corruption over time
  3. Review the typeof window !== "undefined" pattern in SliceSimulator - this should be moved to a useEffect or handled differently
  4. Investigate why router.refresh() causes full page refresh instead of soft refresh
  5. Consider providing a pure Client Component alternative for the SliceSimulator that doesn't rely on Server Component rendering

Workaround Needed

Currently, there is no known workaround. The Page Builder is effectively unusable on Vercel deployments for this project.

Additional Context

  • Bug reproduced on minimal project - No Framer Motion, no useClient, no complex code
  • All slices fail eventually, not just specific ones
  • The issue is 100% reproducible on both Vercel AND localhost
  • On Vercel: Fails after ~3-10 content modifications
  • On localhost: Fails after many more modifications (takes longer to appear)
  • Refreshing the Page Builder temporarily fixes it, but the problem returns
  • Tested on multiple browsers (Chrome, incognito mode)

Metadata

Metadata

Assignees

No one assigned

    Labels

    bugSomething isn't working

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions