next-sanity icon indicating copy to clipboard operation
next-sanity copied to clipboard

Documentation proposal: Remove unecessary context.preview from getStaticProps code example

Open nilsnh opened this issue 3 years ago • 6 comments

In the README.md it says:

export async function getStaticProps({params, preview = false}) {}

The README.md mentions that NextJS preview-mode can be used. But I would argue that it's confusing to include this context.preview variable in the example because it will never be true unless the user sets up the necessary API endpoint.

It really seems like Sanity does not need NextJS's preview mode because it checks to see if the user is logged into Sanity, and relies on that login session to fetch preview data.

So, I propose:

  • Removing unused example code related to NextJS's preview mode.
  • Expand the documentation to emphasise the difference between this way of previewing vs. NextJS's preview mode.
  • Optionally create a separate NextJS preview mode example, and explain when one should consider using it. If there's no need to use it the docs should be explicit about that.

Finally, I'd like to say thanks for building a great product. That's why I took the time to write this up because I think this issue might be a step towards making Sanity previews easier to implement.

nilsnh avatar Apr 24 '21 19:04 nilsnh

Great suggestion, I was quite confused by this as well, and people have already started to copy my confusing code 😅

Maybe also remove the entire getClient/previewClient part as well as it's adding to the confusion and not really necessary for the usePreviewSubscription approach..?

winsvold avatar May 13 '21 10:05 winsvold

@nilsnh can we write a better documentation example as a proposal? I'm so confused by the example that is given.

j2is avatar Sep 23 '21 14:09 j2is

Is there any update on this one? I'm a bit confused about the example given and how it links with the preview mode in nextjs.

rjgux avatar Nov 01 '21 21:11 rjgux

Hey, just wanted to shed some light here, as much for others as for myself, as I've just grasped this concept.

Actually the two different approaches stem from the different ways in which the next-sanity client and the @sanity/client handle authenticating the user:

TL;DR: With next-sanity export async function getStaticProps({params, preview = false}) {} is only indeed needed if you want to _toggle the preview mode on and off. But for @sanity/client, besides preview you actually need to use setPreviewData inside the preview API route, to send the Sanity Token, then grab it like this: getStaticProps(params, preview = false, previewData})

Detailed explanations below:

At first I didn't understand the purpose behind the whole next-sanity package, it seemed simple enough just to use @sanity/client directly in Next.js. But look at the two approaches below and the advantages become clear:

using @sanity/client aka "the hard way"

  • all requests made via sanityClient(options).fetch...etc are NOT authenticated by default. They can only read public data, and can't access draft documents for previews
  • to get access to drafts, you need to provide a token inside of the options object, which is a read token that you need to manually create in the Sanity admin back-end and add as an environment variable inside of next.
  • the env variable needs to be private, not public otherwise you will expose your token. so something like SANITY_API_TOKEN rather than NEXT_PUBLIC_SANITY_API_TOKEN
  • but private environment vars are not accessible to client-side, where you make your preview queries (that's the whole point) so you can only access SANITY_API_TOKEN server-side, which is to say, inside of API routes you create in the /pages/api folder, since they run code on the server, not the client.
  • but this means that, for previews, you actually need to pass the SANITY_API_TOKEN via the API route code you write - most likely like this:
// /pages/api/preview.js
...
 res.setPreviewData(
    { token: process.env.SANITY_API_TOKEN },
    {
      maxAge: 20, // this is optional, to control the amount of time you set the preview mode active for, per request made to `/api/preview`, or whatever you named your preview API route
    }
  );
...
  • then, inside of a page (overly simplified example, would need refactoring for actual use:
// /lib/sanity.js - or wherever you choose to put this
import createSanityClient from '@sanity/client'

const options = {
 <project ID, dataset, API version, etc>
}

export const sanityClient = createSanityClient(options)

export function createPreviewClient(token) {
  return createSanityClient({
    ...options,
    useCdn: false,
    token,
  })
}

export function getSanityClient(preview) {
  if (preview?.active) {
    return createPreviewClient(preview.token)
  } else {
    return sanityClient
  }
}

// /pages/index.js
import {getSanityClient} from "lib/sanity"

getStaticProps(params, preview = false, previewData}) {
    const previewInfo = {
      active: preview,
      token: previewData?.token
    }
    const pageData =  getSanityClient(previewInfo).fetch(<your query here>)
   ...etc
}

using next-sanity, aka "the easy way"

  • the authentication check is done automatically behind the scenes with getCurrentUser()
  • next-sanity's client checks to see if the user is logged into the sanity instance
  • if they are logged in, then **ALL REQUESTS made via a client created with the createClient() method of the next-sanity package will be authenticated, same as having a token priovided via the method above.
  • You don't need to bother with passing the SANITY_API_TOKEN env. variable, at all. It's not needed.
  • simply:
import { createClient } from "next-sanity";
import sanityClientConfig from "lib/sanity-config.js"

getStaticProps(params, preview = false}) { // no more "previewData" needed
    // one line does it all, we just need to turn off the CDN for preview
    const pageData =  getSanityClient(...sanityClientConfig, useCdn: !preview).fetch(<your query here>)
   ...etc
}

This way, logged in users always get the preview data, if your (query is set up to prioritize fetching drafts, that is). You could improve this to allow logged in users to ignore preview data and see only the published data, if that's needed, somehow.

ovsw avatar Jul 10 '22 17:07 ovsw

Hi all,

Just pinging this thread to let you know we're working on updating tooling and new documentation regarding preview mode. You can keep an eye on it here.

And just to clear a few things up:

  • At first the preview mode in Next.js were only used by us to switch statically generated pages into an "SSR"-like mode where fresh draft data and drafts are fetched.
  • It fixed the wonky behavior where you would see old content while the page is loading, then a lot of jank as the drafts loads in once JS is done booting up.
  • Now it's evolved to be more important as browsers like Safari no longer work with this mode as it relies on cookies that use a different origin than the page you're on and the res.setPreviewData({token}) is now vital for Preview Mode to work outside of Chrome. This PR implements a flow like that to enable Safari to run Preview Mode..

stipsan avatar Aug 12 '22 17:08 stipsan

Forgot to mention we've updated the cms-sanity example over at Vercel with more information and an easier setup for Preview Mode that can hopefully help make it easier to grasp while we work on the new docs 😄

stipsan avatar Aug 12 '22 17:08 stipsan

We've just released v2.0.0 which has updated docs that outline how to do things the "easy way" ™ as well as how to provide a viewer token in a secure way. Checkout @sanity/preview-kit for more docs on how to build realtime previews outside of Nextjs' Preview Mode.

stipsan avatar Nov 16 '22 16:11 stipsan