Production-Grade Next.js Production-Grade Next.js

Render Content with getStaticProps

Check out a free preview of the full Production-Grade Next.js course:
The "Render Content with getStaticProps" Lesson is part of the full, Production-Grade Next.js course featured in this preview video. Here's what you'd learn in this lesson:

Scott live codes page error handling using getStaticProps to check for matching slugs in the file systems and demonstrates how to render content using getStaticProps and renderToString to pull the post data.

Get Unlimited Access Now

Transcript from the "Render Content with getStaticProps" Lesson

[00:00:00]
>> All right, so let's actually make this one. So getStaticProps. So first thing we need to do is even so we have fallback true here, but we still need to getStaticProps to be able to handle calls to get blog posts from our cms, even though we didn't statically render our cms.

[00:00:19] We're gonna rely on fallback to do that for us. So the first thing we wanna do is what we've been doing. Basically what's going to happen here that's different though is in this context, we're gonna have params. So these params it's gonna be one of these objects, right?

[00:00:37] So it's basically params.slug, and it's gonna be a slug that came in. So given that slug, we wanna look to see, hey, does that slug exist on one of the blogs in our CMS? Or does it exist on one of our blogs and our file system? Whichever one it is return that one, that's it.

[00:00:52] And that way, it totally works even though we didn't render those paths are the ones in the cms up here. So that's what we'll do. So I'm just gonna use a try catch here. And I'm gonna say, post here. And then the first thing I will do is I'm going to try to see if it exists in a file system first.

[00:01:07] And if that fails, then it means it didn't exist. I'm gonna try to see if it exists in the CMS. If that fails, then how do you wanna handle it? Throw an error or do a soft fail, kinda like what I just described. So let's try to get it from the file system first.

[00:01:21] So I'll say the postsPath just like we pretty much done up here. I feel like we've done it's like 1000 times to this point. So we'll do that. Got the postsPath, we're gonna read the FileSync, the only difference that's gonna be here we don't actually need to read the directory, we just need to get the actual file.

[00:01:42] So I'll say, the file is gonna be path or, I'm sorry, fs.readFileSync like this. With the posts path, actually, let's make a new files path I guess, filePath = path.join because I got to do this here, right? Let's do this. Let's save some code. And I will say posts, and then here I'm gonna get the params.slug, but the slug is not gonna have a .mdx extension on it.

[00:02:22] So I have to add that cuz that's the actual file name has an mdx extension on it. So params.slug, so we'll get that. And then I'll read that filesPath like that. Cool, so we'll do that and then I need to pass in some encoding here. Cool, so we'll say file.

[00:02:42] And actually I'll just set this equal to post, cuz that's what we want. So if that fails, then what I wanna do is come in here and say okay, if that fails, then clearly it wasn't on the file system. So let's try to get it from our cms.

[00:02:58] So to get it from our cms, basically I can maybe try to do some type of regex, which is gross to see if a slug exists inside of the string. I'm not gonna do all that. I'm just gonna convert this to an object using that matter thing that we had, and then check it on there.

[00:03:15] So that means we have to first get all of the posts, and then convert them all over to matter. So I'll just say cmsPosts like this. And this is gonna be, I think I already have I imported. Nop, I don't. So let's import those right quick. So we'll import from content, there we go.

[00:03:41] And we got posts here like that. So these will be posts.published, and we're gonna map over those. And for each one of these posts, I'm going to do the same thing I did before. So I'll get the data = matter from the actual poster, and then it's returning the data like that.

[00:04:09] So we're good there. And then the last thing I wanna do is I just wanna find the one that matches, so I'll say post = cmsPosts. And we'll just do a .find here, or yeah, .find. So then we'll get the post here and it will say if p.slug === params.slug.

[00:04:37] So there we go. So we'll see if one of these match. And if one of these don't match then, I mean, this is where you can just be like, if not post, then I'm just gonna return props right here with like did fail true, no post true. Whatever you want to do, you can put whatever you want there.

[00:04:53] I'm just gonna let this thing error out so we can keep it moving. So now that I got that, then what I wanna do is I need to actually get everything ready for rendering on this page. And the way that that works is with mdx is we have to like create our mdx source, so it can be hydrated on the front end.

[00:05:12] So what we wanna is we want to import the, there's something from mdx-remote/, or let's see was it called, render-to-string. We're gonna get that. So we're gonna say render because it's a render-to-string like that. So we'll get that. The other thing we wanna do is, what's the other one?

[00:05:43] I think that's the only one. So we'll get the render-to-string, and that's what we're gonna use inside of our source. So I'll come down here, and the first thing I'm gonna do is I'm going to pretty much do the same thing. I did up here, where I had to get the matter.

[00:05:59] So right now, this post, I think this one already has the matter on it, let's see, find slug. Now, this one already has the data on it, this one doesn't. So I guess I should do that here. Let's just do the data here. So we'll say, const match, and then I will say post = matter with a match, oops.

[00:06:36] There we go. That'll just give us, making sure it's the same thing as this. They both already have, they both are equals the data. So if I already have the data here, wait, but I need the actual post. All right, I don't wanna do that. Let's go back.

[00:06:50] [LAUGH] Let's just save it to, we'll do the matter down below. We'll just say the post = match like that, or even better back to what I had before. And then down here, We'll do that and, We need to actually get the content here as well. So I'll just return this, Like that, and then this will now be p.slug or p.data.slug equals that, and then now I could just return, match., content, like that.

[00:07:43] Or sorry, post = match.content. Okay, yeah, I just had to get the content so we can actually use it down here. So now that we've got the content, what we wanna do, like I said, we gonna wanna render this to a string, so we can pass it as a source.

[00:07:57] So I'm gonna create some markdown source. So I'm gonna say mdxSource = renderToString. And basically what this takes as an argument, it's not even gonna show me, but I'm pretty sure it takes in the content, which is gonna be the post, so that'll be the string. And then it takes a scope, which is basically like the frontMatter.

[00:08:20] So in this case we need to get the data for it, so I basically had to do this again, which is this thing. Like that, just takes the data, so now we've got mdxSource. So now we can return some props here like this. And we need to make them look like source and frontMatter.

[00:08:45] So I have source here, that's gonna be the mdxSource. And then I have frontMatter which is gonna be the data. Okay, that was a lot. So now that we have that, we can go check out our blog posts actually, hopefully rendering if I wrote the code right, if not, we will figure it out.

[00:09:09] So let's refresh this. So let's click on one that I know should have been statically rendered because it belonged to the file system. So let's click on one of those first. Okay, serializing.source returned from the getStaticProps. I think one of these is async, oops. Is renderToSource async? I think that's a sync.

[00:09:36] Let's try that, says it was a promise, so. Okay, yeah, it was a sync. Okay, so you can see this rendered one of them from the file system, pretty legit, makes sense. So now let's try to go back and render one, not from the file system. And remember we didn't generate the paths from those, so we did say fallback true.

[00:10:00] So it's gonna come in here. It's going to pass in the parameter, and it should match here, if I go and put a log so we can see. Because it's gonna fail here, it's going to fail. If you read from the file system, it doesn't exist, it fails.

[00:10:19] So if we say params.slug here, we should be able to see that match. So I'm gonna click here. And looks like it worked. So if I go look at the logs, what happened here? This was the error from earlier. Here's the log, should match here we-are-hiring, right? It totally works.

[00:10:44] So that's how fallback works, that's how to getStatic passwords, that's all the stuff work. So let's summarize, it's a lot simpler than it looks, all right? What's complicated is the fact that I'm using two different sources for a blog post, and that's what this making complicated. So don't focus on that part.

[00:10:59] What I want you to focus is just the two things, getStaticPaths and getStaticProps. So getStaticPaths, so any static site generate that you'll use, I don't care if it's Next.js, Gatsby, Gridsome, any static site generator you use. If you have a dynamic route and you want those routes to be generated at build time, the framework needs to know all the possible routes ahead of time.

[00:11:24] That's what getStaticPaths does, it notifies Next.js of all the possible full routes for this dynamic path that you want to be built at build time. So getStaticPaths does that. Because our paths are determined from a slug property that exists on some CMS somewhere, we had to hit our CMS to get those slugs to be able to tell the next day guess what those paths are.

[00:11:52] Or in this case, I think we did the file system, I'm sorry, we did the file system in this case. Our blog posts were on the file system. So we had to get the slugs from those to tell Next.js, hey, we're interested and statically rendering these blog posts from the file system with these slugs, that's it.

[00:12:09] Build those for us, Next.js is like okay, I'll build those. Then we put fallback true, fallback true is going to allow us to basically still attempt, and I put attempt, attempt to render a page that was not previously rendered at build time. If getStaticProps actually doesn't error out basically.

[00:12:32] So what's gonna happen is if I go to slash any other slug that isn't here, that slug still going to get passed in on params.slug. It's going to try to read from the file system, it's gonna fail because that blog post doesn't exist in our file system. So then it's gonna try to read it from our cms.

[00:12:51] If it finds it from there, it does the same thing. It renders it to a string, and it returns it. If it doesn't find it here, then it'll error out and you can do whatever you want. Throw an error, send back a new prop that says you error it out, and show something different on your JSX.

[00:13:03] But that's it. StaticPaths is responsible for getting a path that we're interested in at build time. Props is responsible for actually getting the data that matches that path. And fallback allows us to not generate all the paths at once and do them at runtime whenever someone tries to do a request to the page.