Jared Picou

Fri Jan 02

Update from Astro.glob to getCollection

Refacturing my blog to use the more preferred Astro Collection feature.

The Astro logo on a dark background with a pink glow.

Starting with the tutorial

When creating this site, I used the Astro Blog Tutorial as my jump off point. I think it did a good job at showing me the basics of Astro, as well as introducing more features and allowing me to refactor the site along the way. One of the more advanced topics (well - advanced based on my own knowledge level) was getting all of my blog posts and putting them into an array using something called glob.

Astro.glob example

The example used in the tutorial is as follows:


const allPosts = Object.values(import.meta.glob('./posts/*.md', { eager: true }));

This is placed in the frontmatter section on pages that need to utilize all of my .md files, essentially creating an array of posts that can be worked with on the page such as my Blog.astro page. To break down each section of this statement:

const > This declares a variable that cannot be reassigned.

allPosts > The name of the variable being generated. This will hold the results of what is after the = sign.

Object.values() > Returns an array of a given object’s own enumerable string-keyed property values.

import.meta > Meta-property exposes context-specific metadata to a JavaScript module. It contains information about the module, such as the module’s URL.

.glob > A term for a pattern-matching syntax using wildcard characters to match file paths or strings.

'./posts/*.md' > The given pattern that glob is now looking for.

eager: true > This says to load the files found immediately and not lazy load them (loading on demand)

To me, this is a statement that basically says “Find all of URLs found in my /posts/ directory that end in .md and let me work with them”. Then I can use .map to cycle through these posts and list them on my blog page.

So what are Collections

The official documentation can be found here (which will explain the topic much better than i can):

Astro Docs on Collections

In a nutshell, content collections can manage sets of content in an Astro project. This allows you to organize and query documents, enable Intellisense and type checking in your editor, and provide automatic TypeScript type-safety for all content.

You define a collection from a set of thata that is structuraly similar - such as a directory of blog posts which is what we are currently interested in.

To implement content collections, I’ll use that Astro guide and see how it goes!

Changes made to switch to Collections

In learning about this topic, I came across a very similar post to this one where the writer decides to move from import.meta.glob to Astro Collections.

Cassidoo Post on Switching to Collections

I’m sure I missed a few steps along the way as far as documenting what I did. I think the general idea is here and a few extra steps needed to be taken, but still it was a good learning step.

Collection config file

I created a special Astro file to configure my content collections. src/content.config.ts


import { defineCollection } from 'astro:content'; // Import utilities from `astro:content`
import { glob } from 'astro/loaders'; // Import Loader(s)
import { z } from 'astro/zod'; // Import Zod

// 4. Define your collection(s)
const blog = defineCollection({ 
    loader: glob({ pattern: "**/*.md", base: "./src/content/blog" }),
    schema: z.object({
        title: z.string(),
        pubDate: z.coerce.date(),
        description: z.string(),
        author: z.string(),
        status: z.enum(['Draft','Publish']),
        imageurl: z.string(),
        imagealt: z.string(),
        tags: z.array(z.enum(['Astro','General']))
    })
 });

// 5. Export a single `collections` object to register your collection(s)
export const collections = { blog };

Modification of file structure

I then made a new folder src/content/blog to hold all of my Markdown files for my blog posts. I cleaned up other areas of the site as errors appeared.

I also created src/pages/blog/ where before I just had blog.astro. I renamed that to index.astro and added [id].astro. This part was guided by Gemini to create the following:


---
// src/pages/blog/[id].astro
import MarkdownPostLayout from "../../layouts/MarkdownPostLayout.astro";
import { getCollection, render } from "astro:content";
import type { GetStaticPaths } from "astro";

// 1. Generate a new path for every collection entry
export async function getStaticPaths() {
    const posts = await getCollection('blog');
    return posts.map(post => ({
        params: { id: post.id },
        props: { post },
    }));
}

// 2. Get the psot data from props
const { post } = Astro.props;
const { Content } = await render(post);
---
<MarkdownPostLayout frontmatter={post.data}>
    <Content />
</MarkdownPostLayout>

Which for my Blog page, simply gets all of the static paths for my blog posts and slots them in to my MarkdownPostLayout. Before I was putting the MarkdownPostLayout at the top of all my .md frontmatters, but this method just requires it to be imported once and it’s reused for each post. (assumingly)

Summary

After more errors and fixes, it looks to be working! So from my viewpoint, we are using Astro to generate my Blog posts one time. Then we can reference these posts using await getCollection('blog'); instead of the original import.meta.glob() method.

It looks like I’m still using glob, but I’m using it as a loader along with an expected schema which apparently is ‘better practice’. We’ll see how it goes.

This refactoring required some AI guidance, only because the Astro documentation is more matter of fact and not ‘how to refactor to use collections’. I’ll continue to review this topic to better understand it but for now it looks to be working as expected.

References

Astro Blog Tutorial

Mozilla Developer Network

Cassidoo Post on Switching to Collections

Astro Docs on Collections