Astro for Fast Website Development

Content Collections

Jason Lengstorf

Jason Lengstorf

Learn With Jason
Astro for Fast Website Development

Check out a free preview of the full Astro for Fast Website Development course

The "Content Collections" Lesson is part of the full, Astro for Fast Website Development course featured in this preview video. Here's what you'd learn in this lesson:

Jason illustrates the creation of a blog page while discussing topics like structuring, validating, and ensuring type-safety for Markdown content. This is accomplished through the utilization of content collections.


Transcript from the "Content Collections" Lesson

>> Blogs in Astro can be done a number of ways. You can do it with a third-party API. You can do it by just creating pages if you wanted to. And recently, as of Astro 2, you can put in content collections, which are a way of letting Astro kind of do some of the work for us.

I will briefly put this up on the screen. Let's go to the Astro Docs and I'll look up content collections here. And the general idea is that you create a content folder inside your source directory that is identified to Astro as a magic folder, and then you create collections by creating a folder.

So if we want a collection called newsletter, we create the newsletter folder and then anything inside of it is considered a part of that collection. And this again allows us to define a schema so that we can keep the frontmatter of that markdown typesafe. And we can enforce that every entry in a content collection has a slug and a date and an author and a required category.

Or we can just make sure that frontmatter that we don't support doesn't get added in which over time can make our markdown really difficult to manage, because we don't know what's required, what's actually used. The schema can make sure that we're very clear on that. So our case, we're going to set up a blog.

So, we're going to create a new folder called Content. Inside of Content, we're going to create a new folder again, and this one's gonna be called Blog. And up in the the workshop Assets, I have two markdown files. They both are prefixed with numbers. So 01 is our first blog, and I'm going to copy that into the blog folder.

Then 02 is the second one, and I'm going to copy that into the blog folder. So those are going to be our blog posts, and each one has a title, slug, date, description. This is the frontmatter that we can define with a schema. Next, we're going to actually configure this.

So let me make this a little bit bigger. Inside the content directory, not the blog, so top-level content, we want to add a new file, and we're going to call it config.ts. And this is how we configure content collections in Astro. We're going to grab a couple things from Astro content.

And let's see if it's going to give those to me, defined, I think I need to stop and restart. So let's stop and restart. Now that we've got content collections and that's going to generate some types and stuff for us. And you can see here that now it's created like a .astro folder and that's got some of the things that are gonna make our lives a lot easier here.

So then I can get defineCollection and Z, which is their packaging Zod for convenience so that we can define our our schema. So I'm gonna export a const called collections, and that is what Astro's gonna look for to grab out our content collections. Now we can make as many of these as we want.

In our case, we only need the one, so I'm gonna give it a name that matches the folder. So blog, blog. And then we use that defineCollection and pass in an object. And the object has one property that we're gonna use, which is schema. And then we use Zod to actually define it.

So I'm going to do a z .object. This is probably gonna be the case for every content collection, it's gonna start with a z .object. And then I want a title, which will be a z.string(). I want a date, which is a This is something I love about Zod is you get more fine-grained control than putting in a string or something like that.

You can just say, I want this to be a date, and it'll complain. Okay, so he doesn't put a date in it. Whereas if it was a string they could put in whatever they wanted and it would be, yeah that looks fine. So then we want our description.

The description is going to be a string. And because this is going to go, this would be what I would use for like a, Social media preview or something. So I'm gonna set a max of 200 characters, because we want that to something that's not gonna get cut off in the previews.

And that's that. That's a whole content schema. We've got everything that we need to make this thing actually work. Any questions about I introduced a couple of things pretty quickly there anything on Zod? I'm not going to cover Zod it in depth, but I can talk about it at kind of a high level.

I guess to talk about it at a bit of a high level, Zod is a way to do schema definition that is TypeScript friendly. And what makes this really powerful is that TypeScript only works statically. It runs at compile time, right? Runtime, you don't get any TypeScript help because by that point, all the TypeScript has been compiled away.

It gets removed at the build. So, Zod actually continues to do type checking at runtime, which is important when you're dealing with third-party data. Like, if we have an API and we want to pull in some data and make sure that the API didn't change and like give us some errors.

If it did, then Zod would let us do that whereas a TypeScript file wouldn't because it can't, right? Like when I'm running this code in production that TypeScript is gone. And we don't know what the third party API is sending unless we're actually parsing it as it comes in.

So Zod is kind of a it's like an enhancement on top of TypeScript that adds a lot of extra power and a lot of extra type safety, especially when you start crossing the boundaries of services. In the case of Astro, this is really nice because it allows us to do something like type markdown because we're able to parse that frontmatter on the way through.

I don't know that that would be possible without a lot of, it would be challenging to get the kind of type inference that we're getting here without using some kind of a schema definition tool like Zod. So, yeah, question.
>> I don't know if it's outta context, but could you speak a little bit about the difference between something like next.JS, gets static props for creating a blog versus creating a blog with astro

>> Yeah, they're going to be really similar. In fact, I believe that ASTRO even uses get static props as an API method to do this. Unless, let me see. Skip ahead here for a second. It, getStaticPaths. So yeah, they share a lot in common in terms of the API's

>> The functionally, they're kind of the same, you are fetching some things ahead of time and then passing it to Astro or Next in a format that it understands, so that it's able to say, okay, I know that I'm going to get these posts and I need to have dynamic page endpoints.

For each one so that it renders properly or with getStaticProps, you're kind of doing the same thing. Here's all the data that I need. I want that to be available to these pages when they load and you do that ahead of rendering. So from a functional standpoint, you can you can think of them as being roughly the same thing.

And when we get into the implementation here in a second, you'll see why I say that. So the first thing I want to do is actually do like a list of blog posts. So we're gonna create the blog home page. And we'll do that by going into the pages directory, new file, and blog.astro is the name of the file itself.

Give it some frontmatter because this is an Astro component. And I want to import a helper function called getCollection, and this is generated by Astro and the way that they make this work is they set it up like this where it says Astro colon content, and that gives us the helpers that are generated from the schema that we created.

So that's in that .Astro folder, the generated stuff we're getting access to these sorts of things that will then be used to actually generate the page. Next up we want to grab our layout. So this one is going to be our page layout and down below we're gonna grab our blog posts.

So to grab the blog posts we're gonna await and we will get our collection. And this should, if I've done my job right, there it is, autocomplete to show us the available collections. So now we have our blog posts loaded into a variable from our collection, and we can use those then to build out a page.

So we've got our layout. I'm gonna set up a section. That section is going to have a class of blog entries. And inside of it, we're going to loop. So And we'll call this a post. And then we want to return a div class post. And inside of that.

We're going to set up a, we want to show the title. The title in this case is going to be linked. So a Href, and we're going to do a bit of, of kind of string stuff here. So we want our blog posts to live at /blog /the slug.

So we're going to set that up and we'll grab the post.slug. Now, if you note,I did define a slug. Never mind, it will auto generate a slug for you. But if you add a slug in the frontmatter it'll use that instead. So you do have that control that I do that in both I did.

I can maybe show that the other way if we want, then I'm gonna grab the And now data is where, stop, where the frontmatter shows up. So the post itself has a few properties on it. Data is where the frontmatter comes in. And as you can see, as I'm hovering over the top here, we've got the date, title, and description.

And I want to grab out the title in this case, okay? Next, I'm going to set up a time and we'll set the date time property in here. And I want that to be post. Data date to ISO string and that's because we're passing it in as the date time attribute which means that Google and other tools can, parse it as being an actual time.

Kind of thing you have to do but it can be helpful for your. Your various SEO efforts. And then we'll do, this one is a toDateString(), because that means I don't have to format a date manually. And that makes me very happy. And these are browser like platform APIs.

This comes out of Zod because we've told it that it's a date so it makes sure that we get a date object. Next, we're gonna grab the, Description And then we're going to do a little, we're gonna actually just do the exact same thing we did up here.

So I'm gonna copy paste this, grab out that link. But instead of the title, I want it to say, read post. Okay, so this will get the content on the screen, but it's not styled yet. So we want to also add a style sheet. And I can grab that out of the workshop assets.

We've got pages-blog.CSS in here. And I'm just going to drop this in and save it, and get back out here, head over to the blog, and there we go. So that's a Content Collection in action, right, and I think again this is one of the really powerful things about this approach is that, we have the ability here to very quickly stand up something like a blog and if it's I mean granted.

This makes the assumption that everybody who's gonna work on the blog is comfortable with Markdown and pushing things to git. So a markdown based blog comes with a heavy set of caveats for who can actually use and maintain it. But if you're a team of devs, and you're trying to set up a blog and you want that to work, or you've got a team that's very git savvy that maybe aren't devs but are very comfortable with working in Markdown.

You can do this so fast like just a matter of a few minutes you can get a blog up and running. And I really think that's a powerful way to start even if it's not where you eventually land, if you end up landing on a content management system.

And here's the thing that's really cool about this is that, this Swapping this out to be a load all my posts from Contentful or Sanity, you've already written a Zod schema for it. So you can just grab that Zod schema and move the data from Contentful or Sanity or whatever into that same Zod schema.

You don't have to change any other code at that point. You would just be swapping this out to be load from my CMS. Use my schema that I defined and here is my content. And that is a really powerful move, right? So it opens the door for get started quickly and be prepared to continue to grow over time without heavy refactoring burning it down and starting from scratch.

So, now that we've got this blog, everything's looking the way that I want it to, I'm very happy about that. I hope you're very happy about that. And if we click through to one of the posts, we can see that it does use the right, so it uses the slug.

But we haven't built these pages yet. So we're seeing our (404). So the next thing we need to do is build those individual blog pages. And this is gonna introduce dynamic routing in Astro. So you may have seen this if you've worked with a framework like next or remix.

Same general concept, it's gonna look really familiar if you've ever built like get static paths export in Next. So why don't we go ahead and do 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