Headless CMSs with Next.js

Content Blocks & Posts

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 "Content Blocks & Posts" 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 discusses the process of making a dynamic page in a static site using Next.js and a headless CMS. He explains how to create a content model for a customer post, including fields for title, slug, body, and customer. He then demonstrates how to query the customer post data using Apollo and Next.js, and create a dynamic route for the customer post page.


Transcript from the "Content Blocks & Posts" Lesson

>> Scott Moss: All right, so now, some of the things we wanna think about doing, I mean, there's just so much more here that we could add if we wanted to. But I think for the most part, it's very similar to what we've already done. If you wanted to add in, I guess, these testimonials and stuff like that, we could do that, but it's more of the same.

Kind of what we already did, so let's move into something newer. So we have this concept of these customers. They have their logos, and then when you click on it, there is a testimony about it. It's the same one for every single one. It's just one random one that's got created.

They have this testimony here. What we wanna do is we want this to be dynamic. So we wanna be able to eventually load in all these customers. This one's kinda hard, because the design in which they laid it out is broken up by these other widgets here. So if were to look at this, I would imagine it's not just them looping over something.

There's some stopping and then another loop and then some more stopping. So you'd have to have specific list for that. You might make a list for left customers, middle customers, right customers, or something like that. And that way, they can cut kinda control it, or even this way, cuz it's kinda broken up.

So that was a little more complicated. This is where it starts to break down when you have these complicated designs for the content people. But what I wanna do is I wanna be able to go to slash customers and have a slug be up here, any slug, right?

And it loads up that customer's use case, I guess, that's what these are called. These are use cases. And I want the people on my team to be able to edit these use cases with a rich text editor and then have it be rendered here with the same formatting that the rich text editor allowed them to do.

So if they do links, they do bold. If they do headers, if they do block quotes, I want all that to be rendered here without me having to do it. So we wanna do that. So there's a couple of things we need to do to enable that. One, we have to think about this.

This is gonna be a static site. If this is gonna be a static site, that means you would need to pre-render every possible post that you have ahead of time, right? Cuz how else would it be there when someone went to it if they didn't get built if it's static?

It's not gonna be, I mean, you could, just not to stop me from making it just be like a paste that hits the server every single time, and then just rely on caching. But the only interactions you have on here are links that don't need JavaScript. So you should probably statically render this page.

And if that's the case, you need to statically render every possible page ahead of time. There are ways where you can just do, let's say you have 200 E's and rendering 200 of them is really slow during bill, so maybe just wanna render the first 10. And then you still show the links for the other 190.

When someone clicks on the link, then that one will get rendered then. And if someone clicks on the same link, it's already rendered. So it's rendered on demand for the first time. So that's a way you can do it. It was really easy to do that at page's directory.

Do that in the app directory is a little more difficult, so we need to first create our content model. We need slugs. We need to be able to get all the possible slugs that are out there. And then we need to be able to generate, we need to tell Next.js here are all the possible slugs for this route, and then we need to enable rich content editing and rendering here on this page.

So a couple of things we need to do. So let's get started with the actual content model. So for the content model, we need a post. Let's just go look at the shape of this data. I think that's gonna be in, let me see, customer, single post. It's in a single post page.

So if you look in here, there's a lot going on. I don't think we're gonna replace a lot of this. This is the stuff where it starts to be complicated, but we'll replace them, the dynamic parts, the parts that make sense. So won't touch the illustration, won't touch any of this.

So, here we go. We have a title. This is a title. We have authors. We can make those. If we have time, we can make authors, but this is an author. Author has a first name, author has a name, and then a job. So, a job description, and then we have, I guess, this is, what is this.

I think this is that thing on the right. I know this is actually part of the content. Okay, this is the content itself, so gotcha. So this is where the content starts with this paragraph. So as you can see, they're just using regular HTML to do these things, which is great, cuz I think that works perfectly for what we want to do as far as to render.

If this was just one big div, and then it used span tags everywhere to style everything, that'd be really hard. But it's all just HTML with no classes on it, cuz the classes I'm guessing are global. So that's great for us. So we need that. So that'll be the body just related posting.

We won't make that, but I think that's just,
>> Scott Moss: These things at the bottom, these are related. Cuz first, it's how do you figure out they're related? Do we figure that out or do we let content person say these posts are related so they can add them? I don't know.

We also have this thing right here.
>> Student 1: We won't get into that, but-.
>> Scott Moss: I'm all into a vector store and do a semantic search. Yeah, just do a semantic search on the related things, simple, easy. [LAUGH] Just throw AI at it.
>> Scott Moss: Cool, so this page, if you sit on it too long it starts to get janky.

If you just do a hard refresh, it doesn't get janky. I'm guessing has a memory leak somewhere with some animations or something, so it just bothers me. All right, let's do that. We'll go to content model and make a new one here. I'm gonna call this a customer post like this.

>> Scott Moss: I'm gonna add a field. I'm gonna say, what is the title of the post? That's gonna be the entry title. That's gonna be required, that's gonna be unique. And it's just gonna be that easy. I'm gonna make another one called the slug.
>> Scott Moss: It's gonna be short text.

>> Scott Moss: It's also gonna be required. It's also gonna be unique, and it's gonna be a slug. This UI, when you pick slug is not just changing the UI of it. It's a field that gets created from another field, so the content person won't fill out the slug. The slug gets generated from some other field, and we can tell the CMS what field it should be generated from.

In this case, it's from the title. So whatever title you put in for a customer post, that's what the slug is gonna be. It's gonna be URL, it's gonna be formatted in a way where it's safe for an URL.
>> Scott Moss: Slugified, I guess that's the word I'm looking for.

So we got that, we got a title. We got that. We have some more text here. So we have text here. This one's gonna be long text full text search. Wait, hold on, is that a rich text? Yeah, sorry, we want it to be rich text, not text.

So we're gonna pick rich text instead. We will give this a name. I was gonna call this the body of the post.
>> Scott Moss: You can,
>> Scott Moss: Turn on or turn off any specific rich text formatting options for the editor. If you want what's? It's kind of crazy. Well, I guess this makes sense because it's like, if you're a developer and you're the one who needs to render this content and you didn't write the logic to render some of these fields, you need to turn them off until you do.

So I guess that makes sense.
>> Scott Moss: Okay, I'm not linked to entry, linked to assets. Yeah, that's got a nice editting out, why, we believe it, we're not gonna use it. That's pretty cool though. So this is also a required field.
>> Scott Moss: And I don't think we're gonna check any of these.

Really any of that right now. So it's a bridge tax. We'll do that. And then the last thing we wanna do is we wanna attach the actual customer to this post. So that means we need to make another model for a customer. So I'm gonna hit save like this.

I'll make a new content model.
>> Scott Moss: Call it customer.
>> Scott Moss: Add a field. Let's go have a text. It's gonna be what is the customer's name?
>> Scott Moss: Represents the entry title. It is required, it is unique, it's super simple. And then the customer has an image. So I'll just say logo.

What is their logo. So do that, that's also required.
>> Scott Moss: I'm gonna say only an image type. That's pretty cool. I'll give you new dimensions, that's crazy. Confirm that. Now we have that, let's save.
>> Scott Moss: We'll go back to the customer post. Add a new field for a customer.

Which is a reference field.
>> Scott Moss: And this is gonna be one reference. It's gonna be required. And it's only gonna be of type customer, and it's gonna be one of those.
>> Scott Moss: Cool, I'm just gonna move it up to the top. I move it. No, it stayed out here, that's fine.

>> Scott Moss: All right, so we got those things, now we can go make a customer post. I'm just gonna copy the one that's there and then we will be able to edit it. So I add an entry, add a customer post. It's gonna take the one that was already there.

>> Scott Moss: This right here.
>> Scott Moss: Put that there, and then you can see the slug got generated, got slugified. I didn't have to do that, it just happened.
>> Scott Moss: And for the body, I think I should be able to paste that, copy it from the website, not the actual code, but from the website when it's already rendered.

Put it in here and it'll keep the formatting. Okay, pretty sure. So if I copy this and put that in here, yeah, it for sure, keeps the formatting. So we got that, and then the last thing we need is the actual customer. We don't have a customer, so we're gonna make a new one.

This customer is going to be called, who do I have in my, there lemme see what customers we have. We have, I guess we look at the website.
>> Scott Moss: We got Airbnb, Facebook, let's just say this one is Canon. We'll just say Canon. So this customer is Canon.

>> Scott Moss: Add existing media cuz I already have these things uploaded. So I'm gonna pick that one. And there we go, publish that.
>> Scott Moss: Go back here, everything looks good. Publish that.
>> Scott Moss: Cool, now we have a post, a customer post, I guess I should change this to Canon.

So it makes more sense.
>> Scott Moss: Like, not to change if it's generated. Why didn't that change?
>> Scott Moss: Pull this out again. Okay, so now we got that. Like always, I'm gonna go query this in Apollo, and then we're gonna figure out how to generate the pages ahead of time and then also dynamically render something based on a slug.

So let's go do that. So first things first is go to Apollo. Kind of go back.
>> Scott Moss: There we go now, I'm going to look for,
>> Scott Moss: A customer post collection. So I want to be able to get, this is for when we're on the customer slash slug ID, so this would be for one customer post.

It won't be for the list where it shows them all, cuz I don't think the site even has a view where it shows them all. It really is just, you get a list of customers like this and then if they have a post, you can click on it, it'll take you to it.

So we won't worry about this part right now since this is more the same what we've done before, but what we will focus on is this part because it's a dynamic route. Right now it's a hardcoded route that says single post. We're not gonna use that. We're gonna use a dynamic route.

>> Scott Moss: So in that case I want to be able to get one customer post by a slug. That's what I wanna be able to do. So let's do that. So I'll say, go back to Apollo. I wanna be able to get a customer post collection, where,
>> Scott Moss: Slug and then you have to put in the value of the slug that you want, I'll get ours in a minute.

Then I want to get the items, the title, the slug. To get the customer from the customer, I wanna get the logo which has a URL, width, height, title, all those things.
>> Scott Moss: I also wanna get the name of the customer.
>> Scott Moss: And I think I'm gonna get the body.

There we go. And the bodies JSON format. That's how the rich text editor output stuff, which makes it easy to be parsed on any platform. So if you needed to take this rich text output and render it on iOS, you can do that either rendered on the web If you can do that, anything can take JSON and render it so, that's why it's not specific to something like the web where it would output HTML.

So we got that if we run this, we didn't get any things, I actually had to pull a slug in here, so let's go get our slug.
>> Scott Moss: This one,
>> Scott Moss: So I'll add the slug, run this again, and there we go. All this is mostly the body.

The body is just JSON, so you see a lot of that. They put the body at the top, so let me collapse it. There we go. So you see, here's the title, there's the slug, there's the customer, the name of the logo, logo's got a URL, the width and height.

>> Scott Moss: All right, let's copy this. We'll put this in our queries.
>> Scott Moss: We'll say, export const getContentForCustomer post.
>> Scott Moss: Make our query.
>> Scott Moss: And paste this in. There we go, boom, boom. All right, we got that, and then like always, it's just data = await. ContentGqFetcher, where we pass in our query, in this case, we do have variables, we have aware variable.

>> Scott Moss: And aware is like, what is it? Its name, I forgot what it was, let's go look. Slug, yeah, yeah, sorry.
>> Scott Moss: So slug, so that means this needs to take in an argument for slug, slug is string.
>> Scott Moss: So we got slug here. Got our data, if not data,
>> Scott Moss: Throw a new error.

>> Scott Moss: Turn data.
>> Scott Moss: Okay, let's go to types, copy this. Go to our types,
>> Scott Moss: Explorer type, customer post, query, paste that in. That's an object type. This returns an an array of objects, so put an array down there. This is a string, customers and objects, logos and objects, these are strings.

This is a string, so to our numbers.
>> Scott Moss: This is a string, and this is an object with the type of JSON that is JSON.
>> Scott Moss: Cool, so we have our type, everything looks good. Now, all we need to do is go use this in our file. So the file for this is gonna be inside of customers.

So right now, we don't have a dynamic route for this, so we need to make one. So what we'll do inside the customers folder, we'll make a new folder. And we'll say, [slug], like that And inside of that, we'll make a file called page.tsx.
>> Scott Moss: And this is gonna be our page.

So, all I'm gonna do is just take whatever's in a single-post page and bring it there. I could just move that file. I could just move that file up there, but I'm just gonna paste it in. It's the same file.
>> Scott Moss: I'll bring in the related post. It wants to relate a post, so I'll bring it in.

I was getting rid of this single-post folder, which will break some of those ones that you click on that all link to single-post, those will break, but that's fine
>> Scott Moss: Okay, so, we got that,

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