Dynamic Astro Collections

Donnie D'Amato April 10, 2023
Source
When I first heard of Astro it sounded incredible. I thought of it as the next step after working with 11ty. That step is requiring framework support such as React, Vue, or others. While I often avoid frameworks, a framework allows for a shared understanding with a team to achieve a result within the guardrails, even if convoluted. So for documentation sites that expect to host components written in a framework, Astro seems like a solid choice. However, my initial review of the project had some serious critism when finally attempting to build a site. Low maintenance One of the most important qualities of a documentation site is for it to be low maintenance. This can result in lots of different kinds of optimizations but one of the first I think about is how easy it is to create a new page and have it immediately appear in a navigational interface. It’s a non-starter if I need to configure simple navigation by letting the project know that I made a new page and where that new page is in the project. We can do better, but at the time Astro just couldn’t do this without accessing some internal methods and even then it didn’t seem possible without rebuilding the entire site. The ergonomics weren’t great and I decided to pause on the project. Content collections Astro has come a long way since my complaints. Earlier this year with the release of their v2.0, they have introduced Content Collections. Much of their post is talking about it being typesafe which is good but what I was more excited about was the ability to fetch content and manipulate it to render navigation. The documentation suggests a few ways to configure a collection but all of them assume you are manually curating the collection. I’d like to avoid this so you don’t need to provide much instruction past “put files here”. Assume the following project file structure, reduced for brevity. We’ll be discussing each top-level directory and its setup to get this to work. Content The following is an example of how to define a collection from the Astro docs. The most important part here is the file shown below. Note the last comment. The key needs to match your collection directory name. This means that when you add a new directory, this file needs to know and have definitions added. I want to avoid that, so here’s my version: The first new part is using the function from Vite to get all files relative to this config file. We could be more specific here to look only for but this works fine as-is. After using the function to get the files, we parse the filepaths to get the directory that each one is in. This assumes that each section (eg., ) is only one level deep. This will return an array of directory names that we’ll use later. For us, this return . Finally, the array is reduced using the function. This creates a new collection definition with each section in the same way we would have done it manually in the original example. During the build step, Astro will create types based on the schema. Before moving on, make sure you have some content in the markdown files (including the frontmatter) to test out. Pages Next we’ll dynamically render our pages. We’ve set up a dynamic route at . As you might have guessed, the part is the directory name found within from earlier. For our file structure, this’ll eventually write and into the url. The slug is created from the file names in each of these directories ( and ). Here’s how we get that to work using frontmatter in the file which is mostly copied from the official documentation. A few things to note. The alias points to the directory where I have a single as the base layout. The alias points to reusable functions. These aliases can be setup in the file at the root of the project. The function we will need to use twice. Once here to make the paths to content, and a second time to build navigation. I imagine there’s a way for this to run once, but this was easiest for me while I explored this solution. This is what the function looks like: The function from Astro will get the data for a single given collection. We have created a list of collections back at the file as an export called . So we loop over that to get all of the content and write the section it came from within the metadata. After resolving all the promises, we flatten the result because this is a single-level navigational structure. If you need nesting, you might not want to flatten the results here and have your own tree returned. At this point we should be rendering pages dynamically. However, we’d also like to automatically generate a navigational interface from this too. Layouts In the file, we’ll need to manipulate the result of the function to create a tree for navigation. As we loop over the items in the collection, we get the and build the . If you want your navigation to have a specific order, you’d need to either include additional metadata in the markdown files for this function to sort by and/or provide an order to the within the . Components Finally we create a navigation component to consume the . This could be done in any framework, even as an Astro component since it can be a list of links. I’ve chosen to use React to have several subcomponents in the same file. There’s nothing special to see here. The incoming prop is the object that represents the navigation for the content made in the function from earlier. The component will traverse the tree and render out the links. Finally, back in the file, import the and render it in the body passing in the . And that’s it! Now as markdown pages are created in the directory, they are automatically added to the navigation.

Discussion in the ATmosphere

Loading comments...