Astro Blogging: adding Markdown Blog Posts

Rodney Lab - May 16 '23 - - Dev Community

In this lesson you will learn how to get started with Astro blogging.

Astro Blogging

We will now add some blog posts to the website we are building in this Astro Tutorial. In earlier lessons, we created a new Astro site, then added a header and footer using Astro Layouts. We also added a contacts page, sourcing contact information from a JSON file within the project.

Turning to Astro blogging, we will source the post content from Markdown files in a project folder. We’ll take a new approach to generate the post pages. You are going to create a single blog template for all posts (instead of individual .astro files for each one). Astro will use the template to render the Markdown content into pages.

Astro Blogging: First Blog Post

Astro Collections are fantastic for Markdown blog sites. They give us types and autocompletion on front matter fields and also provide some convenient utility functions. Astro looks for Collections in the project’s src/content folder, so create that directory to get started. Then, add a blog directory inside it. Finally, create a new Markdown file, src/content/blog/penny-farthing.md, with the following content:

---
title: "Penny-Farthing"
description: "All about penny-farthings"
date: 2023-04-30T17:11:05.000+0100
draft: false
---

### Some interesting penny-farthing facts

Let’s start with the name…

- the penny-farthing, known as a high wheel in many parts of the world, has a front wheel much larger than the rear one
- the penny-farthing name relates to the relative sizes of the wheels; the penny and farthing were coins used in Victorian Britain, where the cycles were popular
- penny-farthings were phased out as safer alternatives resembling modern cycles were introduced
Enter fullscreen mode Exit fullscreen mode

When you save this file, jump to your text editor and look for a .astro/types.d.ts file. Open this up and scan through it. Astro has generated types for you, for the new post:

declare module "astro:content" {
  // TRUNCATED...

  const entryMap: {
    blog: {
      "penny-farthing.md": {
        id: "penny-farthing.md";
        slug: "penny-farthing";
        body: string;
        collection: "blog";
        data: any;
      } & { render(): Render[".md"] };
    };
  };

  // TRUNCATED...
}
Enter fullscreen mode Exit fullscreen mode

entryMap.blog.data actually contains front matter fields. In the next lesson, we see how you provide types for these (and get rid of that any type).

Astro Blogging: Template

We need Astro to generate a blog post page for each source Markdown file in src/content/blog. For that to work, we need to let Astro know:

  • which content source files we want Astro to use with the template
  • how to generate an output path from the content
  • how to extract front matter fields (like the title) from the source

Astro provides a getStaticPaths method, which will help with mapping content source files to generated pages. Then, Astro Collections will assist us in extracting the description and title from the content Markdown front matter.

Let’s see this in action. We will name the blog post template file [...slug].astro and place it in the src/pages folder. The square brackets indicate this file generates dynamic content. We’ll come back to the name itself in a moment. For now, create the file and add some Astro front matter:

---
import type { CollectionEntry } from "astro:content";
import { getCollection } from "astro:content";
import BaseLayout from "~/layouts/BaseLayout.astro";

export async function getStaticPaths() {
  const posts = await getCollection("blog");
  return posts.map((post) => {
    const { slug } = post;

    return {
      params: { slug },
      props: post,
    };
  });
}
---
Enter fullscreen mode Exit fullscreen mode

There is no need to use file APIs to search for Markdown files in the blog directory; Astro provides a getCollection helper function for accessing the input Markdown content. That helper function generates an array of posts which looks something like this:

  posts: [
    {
      id: 'penny-farthing.md',
      slug: 'penny-farthing',
      body: '\n' +
        '### Some interesting penny-farthing facts\n' + // TRUNCATED...
      collection: 'blog',
      data: {
        title: 'Penny-Farthing',
        description: 'All about penny-farthings',
        date: '2023-04-30T17:11:05.000+0100',
        draft: false
      }
}
      //TRUNCATED...
    }
  ]
Enter fullscreen mode Exit fullscreen mode

Notice, Astro has extracted the slug field (penny-farthing) from the Markdown filename for us. We will use that in a moment to instruct Astro on how to build the post’s route.

getStaticPaths, params and props

We mentioned the getStaticPaths API maps our posts array to the pages we want Astro to generate. For each page, our getStaticPaths function should return a params object and a props object. More generally, props is optional, though required here to pass the title and description into the template.

params fields are used to define the page path; we will use slug as the only param. For our first post, the value of slug is penny-farthing, so ...slug in the template path will be substituted for this value. You can have multiple params fields (for example, to add a year to the path), though one is sufficient here.

To help understand how Astro generates pages from a template, it might be useful to think of Astro replacing the template file with a new src/pages/penny-farthing.astro file. That would sit beside src/pages/contact.astro, which https://example.com/contact will be generated from. For more details on how Astro generates dynamic pages, see the Astro docs.

There are some restrictions on referencing variables in the getStaticPaths function, and also on names for params (param values should be URL-safe). See the Astro docs for more on these restrictions, and more detail on getStaticPaths, params and props.

Astro Blog Post Props

Next, (still within the template file) we need to generate the markup for each post. You have to instruct Astro what to render for blog post pages. For each concrete, generated blog post page, we will use the same BaseLayout that the home page, and contact page used. Add this markup below the front matter:

<BaseLayout description={description} title={title}>
  <main>
    <h2>{title}</h2>
    <Content />
  </main>
</BaseLayout>
Enter fullscreen mode Exit fullscreen mode

The Content component will be supplied by a render function which Astro generates for us, to convert content Markdown to HTML. Add this snippet to the bottom of the template front matter section, to give us access to that render function, via props:

// ...TRUNCATED

type Props = CollectionEntry<"blog">;

const post: Props = Astro.props;
const { Content } = await post.render();
const {
  data: { description, title },
} = post;

---
<!-- TRUNCATED... -->

Enter fullscreen mode Exit fullscreen mode

Astro Collections are helping us out here once more, this time to create a type alias for the template’s props. Notice the template is playing two roles:

  • Earlier, we defined getStaticPaths to tell Astro which pages we want to generate with this template.
  • Now, we are looking at what to render for any particular blog post.

Accessing other Markdown Front Matter Fields

We only destructured the description and title fields from post.data, though the other fields (included in the Markdown front matter) are all available.

Go to http://localhost:3000/penny-farthing in your browser, and you should new see your first blog post.

Astro blog post. Browser screen capture shows the Penny-Farthing blog post, with title and post body.

Adding other Blog Posts

To wrap up, this Astro blogging lesson, let’s add a couple more blog posts. We will use them to create a list of posts for the home page in the next lesson. Create src/content/blog/tandem-cycles.md with this content:

---
title: "Tandem Cycles"
description: "Sharing is caring, why you might try a tandem"
date: 2023-03-30T17:11:05.000+0100
---

### Many… legs make light work?

Did know you that…

- tandem bikes are fantastic for families; no-one has to be get left behind
- if an extra pair of legs isn't enough to get you up hills, try a tandem with electric assist
- the trident is another option for families with two full adult seats and a smaller child one at the back
Enter fullscreen mode Exit fullscreen mode

Then finally, src/content/blog/unicycles.md:


---
title: "Unicycles"
description: "Unicycles: not just for clowns!"
date: 2023-03-30T17:11:05.000+0100
---

### What you might not know about unicycles

Unicycles aren’t just for the circus:

- probably not the fastest for your daily commute, but unicycles were originally used a mode of transport
- great way for many to stay fit, with lower impact on joints than running, for example
- unicycle basketball and unicycle hockey are both actually a thing
Enter fullscreen mode Exit fullscreen mode

Wrapping Up

That’s all for this part of the Astro Beginners’ tutorial. We have the foundations in place for our blog site. We took a first look at Astro Collections and created a blog post template using getStaticPaths. Now you can add more blog post pages to the site just by creating new Markdown files in src/content/blog. You will explore Astro Content Collections a little more in the next lesson. Right now, we have to type the post URLs manually; not ideal for user experience. We will add a list of posts next, to address this. See you there!

. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .