Ultimate Content Collections Overview
Chapters10
An overview of content collections in Astro and why they matter for content sites, with emphasis on build-time data.
A practical tour of Astro’s content collections, showing how to define, load, and render build-time data with type safety and real-world examples.
Summary
Chris from Coding in Public breaks down Astro content collections, starting with why you’d choose Astro over Next.js for content-driven sites. He walks through defining collections in content.config.ts, using the defineCollection function, and ensuring every collection has a loader and optionally a schema. You’ll see three loader approaches — file helper, glob, and an inline (remote/local) loader — plus a glimpse at object loaders for advanced control. The video emphasizes that all collection data is build-time data, even on SSR routes, and demonstrates how getCollection and getEntry fetch type-safe data. A realistic demo shows services, people, and products pulled from local JSON/Markdown or remote sources, then displayed on a homepage with type-safe access and currency formatting. Chris also demonstrates rendering individual entry content with render from astro-content and converting Markdown to HTML. Toward the end, he teases an upcoming feature related to live content collections and teases an “object loader” pattern for consumer-driven data loading. Overall, you’ll leave with a concrete sense of how to structure content.config.ts, choose loaders, and leverage getCollection/getEntry for robust, static data in Astro.
Key Takeaways
- Define each content collection in content.config.ts under a single collections object exported as export const collections = { ... }.
- Use built-in loaders like fileHelper or glob to pull local JSON/Markdown data with a shared metadata schema.
- Optionally implement an inline loader to fetch and transform remote or local data, with a corresponding schema and optional data transforms.
- Object loaders offer advanced control by returning a structured store; they enable consumer-driven data loading and dynamic configuration (e.g., via a URL).
- Get data on pages with getCollection and getEntry to ensure end-to-end type safety across Markdown, JSON, and other sources.
- Render individual entry content using the render helper to convert Markdown to HTML, including inline metadata and body content.
- All content collections are build-time data, which means data is generated at build and served as static data even for SSR routes.
Who Is This For?
Essential viewing for developers using Astro who want to manage content with type-safe, build-time data across local and remote sources, including those exploring object loaders and advanced data shaping.
Notable Quotes
""To define a collection, you need to create a content.config.ts... and it importantly exports one thing, which is an object named collections.""
—Intro to how collections are defined and exported.
""All the data is static essentially""
—Important note on build-time data for content collections.
""You can fetch from an endpoint or hit some kind of SDK or whatever and grab your data""
—Demonstrates loader versatility for remote data.
""Object loaders give you a little bit more structure... you can pass in API keys""
—Highlights the purpose and power of object loaders.
""The get collection or get entry helpers" fetch your data as an array or a single entry with type safety""
—Shows how to access and render loaded data on pages.
Questions This Video Answers
- How do Astro content collections differ from traditional data fetching for static sites?
- What are the best practices for using file, glob, and inline loaders in Astro?
- How does getCollection differ from getEntry in Astro content collections?
- Can Astro object loaders integrate API keys securely for customer data?
- How to render Markdown content from content collections into HTML with Astro?
Astro Content Collectionscontent.config.tsloader file helperloader globinline loaderobject loadergetCollectiongetEntryZodrender (astro-content)
Full Transcript
Why would you use Astro over something like Nexjs? Well, one of the main reasons I do for content sites is content collections and that's what I want to talk about in today's video. You ready? Let's go. Hey, what's up? My name is Chris and welcome to Coding in Public. Okay, so I want to talk about content collections and kind of give you a basic overview of their current lay. And the reason I want to do that is because something new is coming that I'll talk about at the end of the video. This is a brief overview, so we're not going to code all this out, but I do want to show you a preview so that when we look at code, you'll know what you're looking at.
To define a collection, you need to create a content.config.ts or you could use a JS file as well. And it importantly exports one thing, which is an object named collections. Now, this holds all your different collections. And you use this define collection function that Astro gives you to define each collection. Each collection must have a loader. This is how you get the data into the collection. And it can optionally have a schema. Although this is also kind of the whole point is that you can define the structure of how this looks. Now for these loaders, you have a few different options.
You have a built-in one. This is either the glob or the file helper. I'm calling them helpers, but they're just loaders. This is for a local file that is an array of items that all share the same metadata. Or glob is multiple files in the same folder that again all share the same metadata. So these are for local files within your actual repo. You can also write your own custom inline loader. This can be remote data. It could be local data as well, but you should probably just use one of these built-in ones if you want that.
So, you can fetch from an endpoint or hit some kind of SDK or whatever and grab your data, structure it, and then output it on your page wherever you want to. Now, you can also write what's called an object loader, which we will also look at. It's a little bit more of an advanced feature because you can control exactly how everything is stored. So, these are the different types of loaders you can have, and this is how you define a content collection. Now, this is really important. It's all buildtime data. So, even if your site is SSR or a particular route is serverside rendered, when you're pulling data to show on your page, you're going to pull data that you built when you ran your build command.
So, all the data is static essentially. And then finally, to actually show it on a page, you use the get collection or get entry helpers here. These functions, this one takes the name of the collection and gives you all its items as an array. all type safe even if you're pulling from markdown or JSON or anything else. Get entry does the same thing for a single entry. You pass it the name of the collection and the particular ID for that entry and then you can simply display it on your page however you want to do that.
So those are kind of the three things you need to know the config how you actually define the collection that it's build-time data not serverside rendered or anything like that it's plunged from a static store and then you display it by using these things get helper or get collection or get entry. Okay, so with that out of the way, let's look at the actual code. So if I jump over here, we're going to do a couple things. First of all, I'm going to run my server command. This is going to be our fake endpoint where we're pulling remote data from.
And then I also want to run my actual project. Okay, so here's my fake data right here. You can see we've got four products. Then I've also got this homepage that's showing different types of items. Now we've got services, we've got people, and we've got products. So let's see how these are defined over here. Again, I've got a content.config.ts. Let's first of all look at the services. Now remember what I said, you define a collection and they all have to be exported in a export const collections equals and then the name of these. You can define them inside of here, but usually I do it outside because it's a little bit cleaner that way.
You can even put it in a different file and import it and I'll show you that with one in a second here. Now notice every one of them has to have a loader. They don't have to have a schema, but again that's kind of the point so that you get the type safety all the way through the front end. Now for this first one we're pulling from a single file. So, we're using the file helper again. This file right here, services JSON. Let's pull that up. This has an array of items that all show the same metadata.
Now, I've pulled that in and then I've defined a schema. Here are the items I have on here. And I can even do some advanced things here like set a maximum amount or I could trim the ID if I wanted to as well. So, you can do a lot of stuff with with Zod. This is the little helper library that Astro pulls in that allows you to fi to define schema and it's used a lot in JavaScript uh environments. Okay, so this is the first one. That one's fairly easy to understand. This one will actually be just as easy.
I think we're just going to use the glob function where we point it to this directory and say anything that's a markdown file inside of there should have this exact metadata. Again, they all share the same metadata. And if I come over to content up here and we look at our people, we've got Joe and Camille, and you'll see that they've got the same metadata. We've even got some content down here below as well. And if I remember, I'll show you how we use that. Now, the last one here is an inline loader. We haven't looked at object loaders yet.
I'll show you that in a second. So, this is a custom one. Notice we've got the loader. We've also got schema. Now, just to show you some different ways to work with this, you'll see I've got an async function that fetches my remote URL that happens to just be local data. Don't tell anyone. And then what we're going to do is just return an array of items. And because the data is already structured as an array, I can return it immediately like this. Now, notice I'm defining a schema, but I'm doing a couple of things.
Number one, I don't have to have this transformation on here, but I do. Um, but I'm pulling in this product schema which is in a separate file called schemas product schema.ts. It's the same exact thing. We got ID, name, price, category, description, in stock, and date available reflecting everything I've got over here. And then I've actually got some nice helpers here with ZOD where I can infer the type if I ever want to use that somewhere else. So, just to show you, you can kind of define these things, structure them however you want, and then use them.
Notice we're also using this transform. That's because I want this to be a date. However, it comes to me as a string. So, one thing I might want to do is turn it into a date directly here. So, Astro content collections can only get your data, but they can actually manipulate it as well and return it to you in the exact structure you want. Okay. So, these are the three different types here. These two local ones, the glob and the file, and then our inline one. And I'll show you the the object one in just a second.
We're simply exporting them down this way. And then for each of them, I'm going to show them in the front like homepage right here. So, let's go ahead and pull this up. index.astro. And let's look at services first. You'll notice I can actually use a filter method on this getit collections where I pull in the services and then show them down this way. Now again I get type safety everywhere here. So I know exactly the structure of this data and you can see that now I know when I'm working with the data what it is.
You'll see it even knows this is a number. So it knows I can do this currency conversion and turn it into a string that's currency. So that's services. Let's now look at the people. Now people here we've got their image, their name and if I wanted to I could actually display their content, their bios as well. All right, last let's look at the products here. You'll see that these pull in the products and they filter to see just to show the ones that are in stock. For each of these, I simply map over them. And again, I have full type safety.
I can say data dot whatever and it knows that there's an ID here. Now, there's a lot more to content collections than this, but hopefully this gives you a sense of kind of why they're powerful, why they're helpful. Now, the cool thing here is I can click inside of here and I actually come to a dynamic route products one. Now, for that, let me jump over to my pages. And if we look inside of products, I've got this dynamic route called slug. This simply loops through all the different items and outputs ones based on whether or not they have an actual ID.
So if I came to one that doesn't exist, I would get the 404 page because that product does not exist. If it does exist though, I can simply display all the information right here. Again, it knows what type it is and all of that as well. Content collections allow you to manipulate data with full type safety, even if it's a markdown, JSON, or whatever kind of file. Now, the last thing to mention, we've looked at the glob, the file, and the inline. We haven't looked at object loaders and that's because it gives you a little bit more structure.
But to understand why you might want that, let me show you what we have currently. So if I come over here, we can look in the Astro folder, you'll notice that this is what will be produced at build and it's got several things. It's got the schema for all of these defined for me. Importantly though, it's got this data store. Now this data store holds all the static data that's being pulled in, loaded in to be to be used. Now, if you want more control over how things are stored in here or processed in here, this is where you'd use an object loader.
It's also really helpful if you have some kind of service where you're loading data in for a customer because you can allow them to pass in particular things like API keys or anything else. Pass in their own schema and then simply get back the data from you. I've actually got one already called product loader. Let's look at this real fast. You'll notice that it's a little bit different because what we're going to do is export a function and we're going to pass it this loader type. This allows us to make sure we're returning exactly what we need.
It needs a name and then it needs a load function. This load function is going to do a few things. It's going to pull in again the same URL here. Going to check the same check we had before. We're going to grab the data right here. And then we're going to clear the store. Now, this helper here, store.clear, says, "Hey, anything that's in my store currently around this particular um content collection, clear it out, and I want to replace it with whatever we've got here." So, I'm going to loop through each of these items. I don't even have to do this really because they come back as products, but just to show you, I can grab an ID and I can say like set this ID and this is the data and all this kind of stuff.
I've got way more control here. And again, I can do a lot of transformations inside of here. And then I eventually will store this. So, each of these items, I want them to be stored in my store. Here's their ID. Here's the data associated with them. Now, I can also have a schema defined in here as well. So, it looks very similar. We've got the uh the load function and then we've got the schema. This right here can be overwritten by the user. Now, if I were to publish a library like this, notice it pulls in the URL.
So, the user has to pass in a URL to me. Now, that makes it a lot more dry, right? A lot more dynamic. I can use it in a lot of different instances. So, if I were to come back to my content config, we could replace everything we have here uh like this. And let me just comment all that out. And now, we're going to simply define a loader. And I'm going to pull in that product loader right here. And it simply needs to pass in one thing, which is the URL. Now, this was an object, so I'll pass it in like that.
And just like that, everything should work just the same. In fact, if I come over here and refresh, all these items are here. So, this is the power of abstracting this into an object loader is you can have the user pass in something like their API key or something else like that, and as long as they've loaded up your loader, it'll take all that data, do whatever you need to it, update automatically without any more work on their part. I do want to point out one more thing before we look at what's coming next. And that is with our people, we actually had content on those individual markdown files.
So I want to show you how to do that. And while we're at it, we'll also look at the get entry helper because we haven't really done that yet either. So we'll call this guy Joe. I think that was his name. We will simply wait get entry like this. And I'm going to pass in the people and then the different content collection I have, which I think his name was Joe. And the first thing I want to do is just console log Joe. We'll come back here to our homepage and then let me look over here.
Joe was not found. So, let's find his actual ID. What was his ID? Joe Marks. All right. Okay. So, now we should have somebody here. Joe, let me refresh the page. We'll look over here. Yep, Joe's here. Here's the structure of all of these. You've got an ID, all the data associated with that ID. And then, if they happen to have content, you also get this thing called body along with all these other metadata points, including some HTML. And you'll notice it's turned the markdown into HTML. Now, that does mean I can come over here and change Joe here.
Joe marks. We can add a heading. I don't know if you saw that. heading one and then something else down here. And if I were to jump back over here and refresh, you'll notice that now I've got an array of headings where I actually have reference to the individual headings. All of this again is rendered in markdown for me. Now to actually get this into my page, what I'm going to do is first of all check that Joe exists. So let's come down here and let's just say like uh if Joe then I want to render out something.
So what I want to do is grab the individual content we have and I just simply want to display it on the page. So I'm not going to display any of the metadata, just the content. So here I'm going to grab content. We're going to use another helper. This is await render. And this render comes from astro content. And we'll simply pass it Joe. Now it doesn't actually know whether or not Joe can't exist here. So I probably also need to come down here and show the content. So it doesn't yell at me. So I probably do want to do a check up top here and say like if no Joe, then yeah, I can throw an error.
Whatever. Okay, that'll work. And so assuming that I have Joe, I should now show the content. Now notice it actually shows all of its details here including the heading right H1 something. So the content the actual markdown content is converted to HTML and I can actually inspect this just so you can see this and notice it is an H1 heading just my styling makes it look weird here but you can see I've got these things all passed down this way. So that's a little bit more about working with get entry and also how to get content out of a particular entry.
Okay, so that was a very brief overview of how content collections works. There's a lot more involved in it, a lot more you can do with it. I've done several videos in the past if you're interested. Now, I do have some more stuff to say, so I'll leave you with that. I hope you enjoyed that overview of content collections. If you're interested more in live content collections, let me know if I should do a video on what's coming up next. All right, thanks so much for watching. I'll catch you in the next one. Happy goating.
More from Coding in Public
Get daily recaps from
Coding in Public
AI-powered summaries delivered to your inbox. Save hours every week while staying fully informed.





