Preview mode doesn't work for pages not specified by getStaticPaths
See original GitHub issueBug report
Describe the bug
Trying to access dynamic/catch-all routes in preview mode that are not specified by getStaticPaths
returns 404 Not Found in production. I have verified that preview mode cookies are being sent on the requests, so that’s not the issue.
In development, the behaviour works as expected, though this might be due to the fact that getStaticProps
is called on every request when in development mode.
To Reproduce
Repository: https://github.com/nextjs-preview-issue/nextjs-preview-issue
Demo: https://nextjs-preview-issue-demo.vercel.app
- Accessing any pages not marked with “preview only” should render a page that has an appropriate title.
- Accessing any pages marked with “preview only” should render a not found page.
Enable preview mode by clicking the “PREVIEW MODE” button and enter token1
or token2
into the prompt.
- Accessing pages not marked with “preview only” should render a page that has an appropriate title with “(preview)” appended to it.
- Accessing pages marked with “preview only” should render a draft page in preview mode, but it returns a not found page.
See “Additional context” for notable code snippets from the demo repository.
Expected behaviour
From the blog post:
Preview Mode allows users to bypass the statically generated page to on-demand render (SSR) a draft page from for example a CMS.
When trying to access pages that match a dynamic or catch-all route, I would expect that it always renders the page when in preview mode, even if it is not specified by getStaticPaths
, because preview mode is intended for on-demand render of draft pages.
I considered using fallback: true
in getStaticPaths
, but that slightly changes behaviour in production regardless of preview mode - I shouldn’t need the server to check and render pages when not in preview mode.
Running the demo on a local machine in development mode using yarn dev
exhibits the behaviour I expected.
System information
- OS: macOS
- Browser: tested on Chrome, Safari, Firefox
- Version of Next.js: 9.5.2
- Version of Node.js: 10.15.3
Additional context
Notable code snippets/files
Frontend: src/components/Preview.tsx
This function is attached to a button’s onClick
handler which enables preview mode.
const previewMode = () => {
const token = prompt("Enter preview token.");
if (token) push(`/api/preview?token=${token}`)
};
Backend: src/pages/api/preview.ts
This API route enables preview mode and redirects the user to a page that matches the specified pageId
if given, otherwise it redirects them to the index page. In the demo, the pageId
parameter is not used (see above) so the user will always redirected to the index page when enabling preview mode.
Backend: src/utils.ts
This file contains the getStaticPaths
and getStaticProps
implementations used by the dynamic route (blog/[id].tsx
) and the catch all route ([...slug].tsx
).
The getStaticPaths
implementations only lists pages/posts that have published: true
because I do not want draft pages rendered at build time. getStaticProps
retrieves the page’s/post’s data using the slug in the route params regardless of preview mode or not.
When in preview mode, getStaticProps
passes the preview data to the page as the preview
prop. The page will check this prop and render “(preview)” in the title when this is a non-null value.
See: Index page Blog post page Catch-all page
Backend: src/data.ts
This file contains the mock data used in the demo. In a real project these values would be retrieved from a database.
My use case and how I came across this issue
I’m building a page builder interface in my project and I want to allow users to preview pages they’ve created in the CMS that have not been published yet. Pages in my CMS database have a publishState
flag. My getStaticPaths
function only lists pages that have publishState
set to "published"
as I do not want draft pages to be rendered at build time. When draft pages are created, they have a publishState
of "draft"
.
I deploy my project to Vercel and allow the users to publish new content by using a deploy hook to rebuild the project. Before the deploy hook is called, pages that are to be published have the state set to "published"
so that getStaticPaths
returns their ids for rendering during build time.
Issue Analytics
- State:
- Created 3 years ago
- Reactions:8
- Comments:26 (7 by maintainers)
Top GitHub Comments
There’s two ways to solve this:
fallback: false
page and an unmatched slugpaths: []
even iffallback: false
, making preview mode work likefallback: true
I’m not sure which is the most optimal fix, but we’ll think about this. Thanks for the issue!
Update: I was able to enable the desired preview behavior, with only two small changes!
In this case, for a
[slug].tsx
page:fallback: false
tofallback: 'blocking'
ingetStaticPaths()
{ notFound: true }
fromgetStaticProps()
if querying withcontext.params.slug
fails to find dataSince our site is small, we’re doing full SSG instead of ISR, and this code has no impact on the normal production site.