next.js: getStaticProps is not called at request time when preview cookies are set

Bug report

The docs state that when preview mode cookies are set via res.setPreviewData then getStaticProps will be called at request time instead of at build time. But I can’t get this to work reliably in production in dynamic routes with getStaticPaths. The cookies are set correctly, but getStaticProps does not get called.

Describe the bug

I have a headless CMS (Prismic) that generates a preview url that looks like this:

https://my.website.com/api/preview?token={token}&documentId={documentID}

There I have the /api/preview handler that redirects to the appropriate content and sets the preview cookies __next_preview_data and __prerender_bypass

LocalHost/Development All pages are correctly loaded in Preview Mode:

  • index.tsx
  • about.tsx
  • posts/[uid].tsx

LocalHost/Production All pages are correctly loaded in Preview Mode In FireFox but not in Chrome. In Chrome, static routes appear in Preview mode whereas dynamic routes do not. Preview cookies are set correctly.

Server/Production Only static routes appear in Preview Mode for both FireFox and Chrome. Dynamic routes do not appear in Preview Mode even though preview cookies are set.

To Reproduce

// package.json

{
...
   "scripts": {
        "dev": "next-translate && next dev",
        "build": "next-translate && next build",
        "start": "next start -p 8080",
    }
}
// next.config.js

const path = require("path");
const nextBuildId = require("next-build-id");
const { locales, defaultLocale } = require("./i18n.json");

const nextConfig = {
  i18n: { locales, defaultLocale, localeDetection: false },
  images: {
    domains: ["my-domain.com", "sub.my-domain.com"],
  },
  generateBuildId: () => nextBuildId({ dir: __dirname }),
};

module.exports = nextConfig;
// pages/api/preview.ts

import { Client, linkResolver } from "../../prismic-config";

const Preview = async (req: any, res: any) => {
  const { token: ref, documentId } = req.query;
  const redirectUrl = await Client(req)
    .getPreviewResolver(ref, documentId)
    .resolve(linkResolver, "/");

  if (!redirectUrl) {
    return res.status(401).json({ message: "Invalid token" });
  }

  res.setPreviewData({ ref });
  res.write(
    `<!DOCTYPE html><html><head><meta http-equiv="Refresh" content="0; url=${redirectUrl}" />
    <script>window.location.href = '${redirectUrl}'</script>
    </head>`
  );
  res.end();
};

export default Preview;
// pages/posts/[uid].tsx

import { GetStaticPropsContext } from "next";
import { Document } from "prismic-javascript/types/documents";
import React from "react";
import Prismic from "prismic-javascript";
import { useRouter } from "next/router";
import { Client } from "../prismic-config";

interface StaticPropsContext extends GetStaticPropsContext {
  params: {
    uid: string;
  };
}

interface Props {
  story: Document;
  preview: boolean;
}

const Story: React.FC<Props> = ({ preview, story }) => {
  const router = useRouter();
  if (router.isFallback) {
    return <div>"...Loading"</div>;
  }

  return (
    <>
      <h1>Example</h1>
      <main>
        <pre>
          <code>{JSON.stringify(story, null, 4)}</code>
        </pre>
        {preview && <span>This is a preview</span>}
      </main>
    </>
  );
};

export default Story;

export async function getStaticPaths() {
  const client = Client();
  const stories = await client.query(
    Prismic.Predicates.at("document.type", "story"),
    { lang: "*" }
  );
  const paths = stories.results.map(({ uid, lang = undefined }) => ({
    params: {
      uid,
    },
    locale: lang,
  }));
  return { paths, fallback: true };
}

export async function getStaticProps({
  params,
  locale = "en-gb",
  preview = false,
  previewData = {},
}: StaticPropsContext) {
  const { ref } = previewData;
  const { uid } = params;

  const client = Client();
  const story = await client.getByUID("story", uid, {
    ref,
    lang: locale,
    fetchLinks: ["author.name", "author.image"],
  });

  return { props: { story, preview } };
}

Expected behavior

Preview cookies should result in getStaticProps being called at request time.

System information

  • OS: MacOS 10.15.7
  • Browser: Chrome/FireFox
  • Version of Next.js: 10.0.3
  • Version of Node.js: 12.18.3
  • Deployment: Digital Ocean App Platform as Web Service (not Static App)

About this issue

  • Original URL
  • State: closed
  • Created 4 years ago
  • Reactions: 3
  • Comments: 21 (18 by maintainers)

Most upvoted comments

Ok, so running NODE_ENV=development npm start also resolves this issue on my local machine, but not in production where requests with the cookie are not reaching the server because they’re getting cached in Cloudflare.

A hacky workaround might be to adjust your preview route’s link resolver so that it adds a random query string to the end of the URL.

Cloudflare is not the culprit, or at least not the only one. On my local machine, where there is certainly no Cloudflare involved:

_ next dev next start
Firefox preview works preview works
Chrome preview works preview does not work

…unless you’re saying Prismic itself is using Cloudflare?