Headless CMSs with Next.js

Adding Content Preview Route

Scott Moss

Scott Moss

Superfilter AI
Headless CMSs with Next.js

Check out a free preview of the full Headless CMSs with Next.js course

The "Adding Content Preview Route" Lesson is part of the full, Headless CMSs with Next.js course featured in this preview video. Here's what you'd learn in this lesson:

Scott guides students through the process of enabling draft mode in a Next.js application connected to Contentful. He explains how to create an API route, set up a secret for authentication, and handle the logic for redirecting to the previewed content. He also demonstrates how to pass the draft mode parameter to GraphQL queries to fetch draft content.


Transcript from the "Adding Content Preview Route" Lesson

>> Scott Moss: Okay, let's enable this. So let's go to our code, we're gonna make API route. So API, you can get rid of this hello one, doesn't do anything, make a new folder and app/api, called draft. Make a file in there called route.ts. And what we wanna do, actually, I forgot we need to add our secret.

So let's go back to our settings, our content preview. Let's click on the one that we made. We need to put our secret here, so I'm gonna put a query string. I'm gonna say, I don't know, secret, and set that equal to whatever your secret was in your environment variable.

So I'm gonna grab mine, and this is use defined, you can put whatever you want. I'm just gonna put this, so there we go, this way we know it came from contentful. So we got that.
>> Scott Moss: And basically the strategy is, I'll walk you through it, cuz it sounds more complicated than it actually is.

>> Scott Moss: Basically, the strategy is, we're gonna get the secret from the params. If you don't have a secret, who are you? Get out of here. So we're gonna say no. If the secret doesn't equal the secret we have in our environment variable, then also, did we type that in on CONTENTFUL wrong.

Or does someone know that we need a seeker and they're just guessing at this point, either way, that's terrifying. And then lastly, you might have some logic here where if you added some of those interpolated values on the URL to get this hero ID, get this entry ID, you would use some server side queries to get that.

But in our case, our hero is gonna to be on the homepage, so we just need to just redirect to the homepage. And then we just need to tell Contentful on that server component to use previews and then we should be good, but not before enabling draft mode.

So let's do that. So here we will go to our route, that's right. So we'll say, const GET =() a function export, or excuse, just export good, there we go. This is gonna be async or, I don't think any spacing there actually, looking at a request, this is a type NextRequest from next/server, and then we will get the searchParams from a new URL.

And we'll pass in request.url. We'll get the searchParams. And then we can say, secret = searchParams.get(secret), that's the parameter we put on the end of the URL, or at least that's what I put on the end of my URL. In my preview settings in Contentful, I had a query string, secret = mySecret, whatever your variable was, that's what you put here.

So I'm going to get my secret. I'm gonna say, if not secrets, then return new Response.
>> Scott Moss: I do a status on here. New Response().status? No, that's not it. It's just the second argument, okay. So we'll say, No, {status: 400}, you didn't send the right thing. If secret does not equal process.env.whatever that thing is, CONTENTFUL_PREVIEW_SECRET,
>> Scott Moss: Then it's very similar.

Just different status of, all right, you said the right thing, but it's not right. So you got that.
>> Scott Moss: And then the last thing we need to do is enable draftMode, so we can get draftMode from the Next.js import from next headers. Which basically just, it really is just a cookie that sets like draft, you don't even really need to use it, you can do it yourself.

You can just set any cookie that you want, give it a value to true, look for that cookie on the other side, and then pass it into your server side queries. But we'll get it from next headers. DraftMode, so we'll say draftMode().enable(), like this. And then we will redirect from Next Navigation to Home.

>> Scott Moss: And we're going home because that's where the hero page is, or the hero section is.
>> Scott Moss: You wanna redirect the user to where the content they're clicking preview on would be rendered so they can actually see that. So you can tell it can get pretty complicated if you have some content hidden behind an interaction.

So you redirect them there but you gotta click this drop down and click this button and then they'll see the content. So that can't be helped, but it works pretty good. This is bare minimum this is bare minimum. And this is for the really savvy content creators that understand technology.

For people who are just pure content, this is probably not enough for them, they're gonna need some like Squarespace where they can click on the website and edit it in line and do stuff like that. This isn't gonna be good enough for them, in my experience, as someone who's built this and tried to sell it.

>> Speaker 1: Because even if there's a sub page that has a hero, right?
>> Scott Moss: Yeah, exactly, exactly, because they don't know exactly, like, even if you bring them to the page, they still have hard time figuring out where on the page this thing actually is. Whereas an editor where you can click and edit, it makes sense, it shows you borders when you click on it, it's just more intuitive.

So just, it has to be somewhat really savvy.
>> Speaker 1: Like the mental model.
>> Scott Moss: Yeah, exactly. So, this is like bare minimum. Okay, so we got that, so let's test that at least it redirects, right? Let's test that part. So let's go back to here, open this tab.

>> Scott Moss: Cool, it redirects. So that means the secret works, the cookie, all that stuff works, let's check to see if our cookie's there. So I'm gonna go to Application, I'm gonna go to Cookies. And yeah, so it's actually this cookie right here, prerender_bypass, that's the draftMode cookie, it's just prerender_bypass.

Cuz that's what the draftMode does, it bypasses the prerender. Let's actually get the official
>> Scott Moss: Thing from Next.js on what draftMode does. So basically, static rendering is useful for your pages, when you fetch data from a headless CMS. However, it's not ideal when you're writing a draft on your CMS and you wanna view the draft immediately on your page.

You want Next.js to render these pages at request time instead of build time and fetch the draft content instead of the published content. You'd want Next.js to switch to dynamic rendering only for this specific case, yeah. Because all of our pages are going to be rendered statically. If you want to say, I mean, actually, can you just be dynamic, which basically means don't cache this, go and fetch the new one every single time.

Be dynamic as if I used cookies, as if I used headers, as if I said no cache, basically. So, that's what it's gonna do. So the re-rendering or the redirecting works, now what we need to do is if we go to our hero,
>> Scott Moss: We can get the draft mode in here, so we can say draftMode from next/headers and then we can say, we need to make this function, getContentForHero.

Take in an optional draft. So we'll say isDraft, and this can be a boolean. All right, I'll just say, default to false actually. So it'll say isDraft, and then what we need to do if it is a draft, we need to pass that in as an argument to our GraphQL query.

And then also here, because this content GraphQL takes in a preview true or false, so it can switch with authorization token it has. Because you cannot access preview content with the access token, you have to use the CONTENTFUL_PREVIEW_ACCESS_TOKEN to access preview content. So let's fix that. So there are a couple ways to pass it in with GraphQL.

There's the right way, there's the way we're gonna do it. So, the way that we're gonna do it [LAUGH] is we're just gonna just skip this and we're just going to go straight here. And we're just gonna say, so luckily for us, if we go, just to show you, and also just to make sure I'm not insane, every query from Contentful should take in a preview like this, and it could be true or false.

So every single query takes in a preview, true or false. So what that enables us to do is to go here and say, okay, preview, cool, and then we can interpolate and say, isDraft. So if isDraft, then we want true and double quotes and false if not in double quotes.

It has to be that, has to be the double quotes. Graphical syntax, this has to be in double quotes.
>> Scott Moss: So we got that, and then down here, we can say preview. It's gonna be bound to isDraft.
>> Scott Moss: Cool, so now we have that. So this will enable us to get the draft content.

>> Scott Moss: If we set it up right. Who knows? You can also pass this all the way down cuz like, all right, it'll get these items, these will be draft but then like, what if you want to get the draft version of these two? So you could also come down here and say, yeah, I'll do the same thing, and it cascades, you'd have to do it that way as well, for relations.

Okay, so, let's make sure nothing broke, first of all. Let's go here. Let's go Home. Nothing seems to be broken there, let's go back to our CMS. That's cool, let's open this up again. That seems to be cool. Okay, nothing seems to have broke, I think our cookie is still set.

>> Scott Moss: It is, okay, and let's actually make a draft.
>> Scott Moss: Which, you don't do anything to make a draft, you just, you know you have a draft when it says Publish Changes, that's how you know you have a draft. If didn't have a draft, it would have said it was published.

So we have a draft, this is my draft. If I want to preview this, if we set this up, this should do it, but looks like it didn't. Let's see
>> Scott Moss: Let's check out if, wait, I didn't pass it in, did I? Yeah, I guess we gotta do that.

So let's go here and we'll say, isDraft = draftMode.isEnabled, like that. So let me say isDraft, there we go. And it showed up immediately, as soon as I hit Save. So let's just go through it all again. So we go here, here's our draft. Let's just change it again.

>> Scott Moss: Kinda draft here. Wait for it to save, it just saved. Open this up, and it didn't show that one, it's showing the previous one. So maybe it didn't save yet, let's give it some time, I guess.
>> Scott Moss: There it is, I guess it just takes a minute for Contentful's preview or draft content that goes to their CDN, I guess I didn't expect the consequence to click that quick.

So it just took a minute, but areas, so now you can see the draft. Typically what I would do is, when I detect that you're in draft mode, I'll add a UI somewhere on the app. I'll put a border or something, or a button or something that says, you are in preview mode, this is not live.

Because right now we're doing this on a local host, but you're gonna set this up on the production app. The concept people aren't gonna run a local host version of a website. So the URL that we put in for the preview would have been yourwebsite.com/api/draft. It would have been local host.

So just to let them know that I would put a button there that says, you are in draft, and I'll put another button next to it that when you click it, it clears the cookie to say, okay, cool, I don't look at the draft anymore. And that's the experience that you wanna show.

And to clear the cookie, it's pretty simple, you just make another route, and it just calls draftMode().disable(). That's it, you just make a route that does that. That's it, and that clears the clicker. So, put a button on your page that makes a call to this and clears the cookie.

Learn Straight from the Experts Who Shape the Modern Web

  • In-depth Courses
  • Industry Leading Experts
  • Learning Paths
  • Live Interactive Workshops
Get Unlimited Access Now