29
Astro + Netlify CMS
I like markdown as much as the next developer, but it's easy to forget how convenient a CMS can be. For personal projects or internal tools, though, I can't justify reaching for a full headless CMS setup. Configuration takes time and, more importantly, for Jamstack sites it can be a huge time saver to have the entire CMS alongside the source code right in git
.
We're huge fans of the git-based CMS idea at Navillus. When the entire site is built to be deployed as a static site it really doesn't make much sense to need to pull from a remote server to load content, and you're already working in git
!
Our go-to content solutions:@NetlifyCMS for internal projects (when we just need it to work)@forestryio for client projects (when a clean, user friendly UI is a must)@fauna (when a file based CMS just won't cut it)
— Navillus (@navillus_dev ) June 20, 2021
What tools are you always reaching for?
For those that follow us on Twitter, today's blog post won't be much of a surprise. Netlify CMS is the first content management tool we reach for on side projects and internal tools. It's dead simple to setup and deploy, and is deployed alongside our site as static HTML. And before you as, yes we are loading the admin panel's JS bundle from a CDN but you can actually install that via NPM if you prefer.
This demo is based on the excellent eleventy-netlify-boilerplate demo. If you're interested in 11ty as well, I strongly recommend you take a look at that repo to learn best practices when setting up an 11ty project!
Our goal today is to highlight the Astro-specific details when integrating with Netlify CMS, so I won't be diving too far into the initial setup. Check out Netlify CMS's excellent docs for adding the CMS to your own site for a quick rundown.
For this demo, I decided to load the netlify-cms
library from CDN, but as mentioned in the docs, you can install from NPM instead. In that case, Snowpack will handle bundling the JS in production builds.
When including /admin/index.html
and /admin/config.yml
, you can simply copy those files from the docs to your Astro project's /public
folder. Astro includes everything in the /public
directory as static assets, for example your /public/admin/index.html
file will be available when navigating to yoursite.com/admin
.
First thing's first, lets setup support for blog posts.
Once you have the CMS and Netlify Identity all setup, it's time to start adding content. If you take a look at our demo repo, you'll see that all of the blog posts are saved to the /src/pages/posts directory.
For Netlify CMS, The key is to make sure that your config.yml
is pointing to the correct folder.
collections:
# Our blog posts
- name: "blog" # Used in routes, e.g., /admin/collections/blog
label: "Post" # Used in the UI
folder: "src/pages/posts" # The path to the folder where the documents are stored
create: true # Allow users to create new documents in this collection
slug: "{{slug}}" # Filename template, e.g., YYYY-MM-DD-title.md
fields: # The fields for each document, usually in front matter
- { label: "Title", name: "title", widget: "string" }
- { label: "Publish Date", name: "date", widget: "datetime" }
- { label: "Author", name: "author", widget: "string", default: "Anonymous" }
- { label: "Summary", name: "summary", widget: "text" }
- { label: "Tags", name: "tags", widget: "list", default: ["post"] }
- { label: "Body", name: "body", widget: "markdown" }
In this excerpt from the demo's config.yml
, note that folder
is pointing to the correct directory.
Loading local data is handled with the Astro.fetchContent API.
export let collection: any;
export async function createCollection() {
return {
/** Load posts, sort newest -> oldest */
async data() {
const allPosts = Astro.fetchContent('./posts/*.md');
return allPosts
.sort((a, b) => new Date(b.date) - new Date(a.date));
},
/** Set page size */
pageSize: 10,
}
}
That's really all there is to it! The fetchContent
API takes care of loading all matching markdown files. I left out RSS feed support here for brevity, but you can find that in the demo repo here.
<Layout title={title} description={description}>
<h1>{title}</h1>
{collection.data.map((post) => (
<PostPreview post={post} />
))}
</Layout>
Here the $blog.astro
template is taking the data loaded above and rendering a list of post previews. If you have experience with React (or JSX) this will feel very familiar. Brackets {}
are used to escape plain old JS into the template, mapping over the posts loaded in data()
and passing the data of to the PostPreview
component.
Take a look at one of the sample blog posts in the demo repo. It defines the template used to render a blog post in frontmatter, just like you may have used in 11ty, Jekyll, or really any other static site generator out there.
Astro is still in beta and one of the big updates coming down the pipe is an update to dynamic routing. We'll skip past the routing setup for now as that may very well change in the near future, but feel free to poke around in the demo repo or ask us questions on Twitter!
I won't go into detail here on how /author/:id
or /tags/:tag
routes for now, but keep an eye out for a follow-up blog post once the routing APIs are finalized!