Destiner's Notes

Draft Publications and Post Scheduling in Astro

19 February 2022

When I blog, I like to write a few posts in advance and post them later one-by-one. This way, I can create at my own pace and not bombard the blog with 3-5 posts at the same time.

Thus I was looking for a scheduling solution. Ideally, it shouldn’t require signing into some third-party service and should work automatically as much as possible. Below is the result of my research so far.

Draft posts

Astro has a built-in feature for draft posts. To mark any post as a draft, add draft: true to the frontmatter of that post.

Note: this will only affect the production build. You will still be able to access draft posts in dev mode. Also, as said in the docs, Astro.glob() will include draft posts, so you might need to filter them when building an index page.

Draft posts are enabled by default, but if you wish to specify that explicitly, you can set buildOptions.drafts to false in the Astro config.

Scheduled posts

Draft posts are a great feature to exclude WIP posts from the final build. But what about scheduling the posts that are complete? For that, you’ll need to use a different solution.

First, for each post provide the publish date in the YYYY-MM-DD format (e.g. date: 2022-03-19). You can also provide a time. By default, the UTC timezone is used.

Next, on your index page, filter out “future” posts:

const allPosts = await Astro.glob<PostMetadata>('./post/*.md');
const posts = allPosts.filter((post) => {
  const isScheduled = new Date(post.frontmatter.date).getTime() > Date.now();
  return !isScheduled;
});

One downside of that solution is that scheduled posts will still be built at the moment you deploy your site, they just won’t be included on the index page.

Hidden posts

As a side note, you can also use “post filtering” technique to hide some posts from the index page. Say you wrote a highly specific post (e.g. one that provide solution to an error that happens in an edge case scenario). You can set hidden: true in the frontmatter of that post, and then filter it on the index page:

const allPosts = await Astro.glob('./post/*.md');
const posts = allPosts.filter((post) => {
  const isHidden = post.frontmatter.hidden;
  return !isHidden;
});