Can't get Side-by-side Web Previews plugin to work with NextJS app dir on Vercel

Hi. I’m having big trouble getting the side-by-side Web Previews plugin to work. I have a /api/draft route that sets draftMode to true and I have a /api/preview-links route for the plugin. When editing a page in the CMS, the sidebar pops up with the preview site but the problem is that it is not server side rendered, it is the static build of my site. However, when I copy the URL of the preview site by clicking the icon in the top right of the preview window, and paste that link into my browser, the site gets server side rendered and everything works as it should. What could be the problem causing it not to work in the preview? When debugging, I validated that the draftMode does never equal true in the side-by-side preview window, whatever I try to do. But it seems that the cookie “__prerender_bypass” gets set in the side-by-side preview.

Hello @simon3

You can use this snippet on your endpoint to make sure that the cookie is set and not lost:

  //to avoid losing the cookie on redirect in the iFrame
  const cookieStore = cookies();
  const cookie = cookieStore.get('__prerender_bypass')!;
  cookies().set({
    name: '__prerender_bypass',
    value: cookie?.value,
    httpOnly: true,
    path: '/',
    secure: true,
    sameSite: 'none',
  });

I tried pasting that code into both the api/draft and api/preview-links endpoints but it didn’t help.
Here’s my current code:

//   /api/preview-links
import { SchemaTypes } from "@datocms/cma-client-node";
import { cookies } from "next/headers";
import { NextResponse } from "next/server";

const corsInitOptions = {
  headers: {
    "Access-Control-Allow-Origin": "*",
    "Access-Control-Allow-Methods": "POST, OPTIONS",
    "Access-Control-Allow-Headers": "Content-Type, Authorization",
  },
};

const baseUrl = process.env.VERCEL_URL
  ? // Vercel auto-populates this environment variable
    `https://${process.env.VERCEL_URL}`
  : // Netlify auto-populates this environment variable
    process.env.URL;

/*
  This endpoint is for the Web Previews DatoCMS plugin:
  https://www.datocms.com/marketplace/plugins/i/datocms-plugin-web-previews

  After installing the plugin on the project, insert the following frontend settings:

  Name: Production Website
  URL: <YOUR_WEBSITE>/api/preview-links
*/

function generatePreviewUrl({
  item,
  itemType,
}: {
  item: SchemaTypes.Item;
  itemType: SchemaTypes.ItemType;
}) {
  switch (itemType.attributes.api_key) {
    case "insight":
      return `/insights/${item.attributes.slug}`;
    case "insights_page":
      return "/insights";
    case "homepage":
      return "/";
    default:
      return null;
  }
}

export async function OPTIONS(request: Request) {
  return NextResponse.json({ success: true }, corsInitOptions);
}

export async function POST(request: Request) {
  const requestBody = await request.json();
  const url = generatePreviewUrl(requestBody);

  if (!url) {
    return NextResponse.json({ previewLinks: [] }, corsInitOptions);
  }

  const cookieStore = cookies();
  const cookie = cookieStore.get("__prerender_bypass")!;
  cookies().set({
    name: "__prerender_bypass",
    value: cookie?.value,
    httpOnly: true,
    path: "/",
    secure: true,
    sameSite: "none",
  });

  const previewLinks = [
    {
      label: "Published version",
      url: `${baseUrl}${url}`,
    },
    {
      label: "Draft version",
      url: `${baseUrl}/api/draft?redirect=${url}`,
    },
  ];

  return NextResponse.json({ previewLinks }, corsInitOptions);
}
//   /api/draft
import { cookies, draftMode } from "next/headers";
import { redirect } from "next/navigation";

export async function GET(request: Request) {
  const { searchParams } = new URL(request.url);

  draftMode().enable();

  const cookieStore = cookies();
  const cookie = cookieStore.get("__prerender_bypass")!;
  cookies().set({
    name: "__prerender_bypass",
    value: cookie?.value,
    httpOnly: true,
    path: "/",
    secure: true,
    sameSite: "none",
  });

  // Redirect to the homepage, or to the URL provided with the `redirect` query string parameter:
  const redirectUrl = new URL(
    searchParams.get("redirect") || "/",
    `https://${process.env.VERCEL_URL}`
  );

  redirect(`${redirectUrl.pathname}${redirectUrl.search}`);
}

I see, thank you!

We have a starter

That has WebPreviews installed, and it uses the appDir, so if you’d like, you can copy the implementation from there: https://github.com/datocms/next-minimalistic-photography/blob/main/app/api/web-previews/route.ts

As it should work exactly as it shows in the starter

The starter you linked does not use NextJS draft mode, and my current code is copied from this starter Next.js Template blog - Start a Next.js blog in minutes which does use NextJS draft mode. But as I said, it already works perfectly in my browser, just not in the iframe which is the weird part.

Sorry @simon3! I meant to link to another starter, we’re actually about to release this week a starter that has this exact example (with draft with webpreivews & with appDir) I’ll get back to you as soon as it is out

1 Like

Hi,

I’ve now migrated my code using the code in your latest starter template, but the problem still persists. The iframe side-by-side preview does not display the unpublished version of my site using the draft mode in NextJS. It does however work when I visit the site with my browser (Chrome v117.0.5938.92) through the /api/draft/enable?url=/&token=secretToken endpoint. I tried debugging the issue and would you believe it, the draft mode side-by-side preview correctly works in Firefox! I then tried it in Safari, but just like in Chrome, it didn’t work, which is a bummer. Seems like some issue with the iframe and the NextJS draft mode __prerender_bypass cookie.

1 Like

Are you looking into this? Would be nice to have side-by-side preview work on Chrome and Safari.

Hello @simon3

Using chrome Version 116.0.5845.187, in the exact code inside the starter https://www.datocms.com/marketplace/starters/next-13-company-landing-page-demo the iFrames seemed to work as expected for the preview:

If you are using a cloned project from that starter, perhaps an extension or a cache conflict or specific configuration of your browser is causing the issue?

Hi, we have exactly the same problem, with Chrome and Safari. In them only published records can be viewed in the side by side web preview. We are also using NextJS and it’s draftMode. When visiting the previewURL in a separate browser tab it works, but when visiting it side by side we get a 404 error for not published records.

Also the link to the “Next 13 Company Landing Page Demo - DatoCMS” is a 404.