DEV Community

Cover image for Astro v5 Blog starter
jldec
jldec

Posted on • Edited on • Originally published at jldec.me

Astro v5 Blog starter

Astro v5 blog starter

I recently moved a friend's blog to Astro v5. The motivation behind selecting Astro was its first-class support for markdown content.

With just a minimal amount of boilerplate, Astro will validate markdown frontmatter, generate static pages for each post, and optimize all the images in your posts.

https://astro-v5-blog-starter.jldec.me/blog/first-post

Markdown

---
date: '2024-12-01'
title: 'My First Blog Post'
image:
  src: '../images/birch-trees.webp'
---

## Markdown is

- a lightweight markup language
- for decorating plain text
- with things like headings, lists, links, and blockquotes
- making minimal assumptions about formatting.

#### Here is an inline image:

![sunset](../images/sunset-cambridge.jpg)
Enter fullscreen mode Exit fullscreen mode

HTML

<img
  src="/_astro/birch-trees.DrRha0ED_9bfrU.webp"
  alt=""
  width="2016"
  height="955"
  loading="lazy"
  decoding="async"
  class="w-full h-60 object-cover object-bottom"
/>
<article class="prose mx-auto p-4">
  <h1>My First Blog Post</h1>
  <a href="/">&lt;&lt; Back</a>
  <h2 id="markdown-is">Markdown is</h2>
  <ul>
    <li>a lightweight markup language</li>
    <li>for decorating plain text</li>
    <li>with things like headings, lists, links, and blockquotes</li>
    <li>making minimal assumptions about formatting.</li>
  </ul>
  <h4 id="here-is-an-inline-image">Here is an inline image:</h4>
  <p>
    <img
      alt="sunset"
      width="1343"
      height="683"
      loading="lazy"
      decoding="async"
      src="/_astro/sunset-cambridge.7ZAluiBF_15WAVg.webp"
    />
  </p>
</article>
Enter fullscreen mode Exit fullscreen mode

Starter Repo

Here is the result of extracting these key features into a new blog starter. The repo does not include a lot of design - just the configuration and a minimal amount of code.

https://github.com/jldec/astro-v5-blog-starter

The project includes:

File Structure

├── LICENSE
├── README.md
├── astro.config.mjs
├── package.json
├── public
│   ├── _headers
│   └── favicon.svg
├── src
│   ├── assets
│   │   └── astro.svg
│   ├── components
│   │   └── AstroLogo.astro
│   ├── content
│   │   ├── blog
│   │   │   ├── 2nd-post.md
│   │   │   └── first-post.md
│   │   └── images
│   │       ├── birch-trees.webp
│   │       └── sunset-cambridge.jpg
│   ├── content.config.ts
│   ├── layouts
│   │   └── Layout.astro
│   ├── pages
│   │   ├── 404.astro
│   │   ├── blog
│   │   │   └── [id].astro
│   │   └── index.astro
│   └── styles
│       └── global.css
├── tailwind.config.mjs
├── tsconfig.json
└── wrangler.toml
Enter fullscreen mode Exit fullscreen mode

Markdown driven blog

Markdown blog posts live in src/content/blog.

The schema for the blog collection is defined in content.config.ts. This file also includes utility functions for sorting and filtering posts. E.g. if the draft flag is set, a post will only be included during dev, but not published with the production build.

The home page is defined in src/pages/index.astro which lists posts in date order.

Posts are rendered by the dynamic route src/pages/blog/[id].astro. In order for Astro to pre-render static pages, dynamic routes export a getStaticPaths function which returns a list of params and props for each rendered route.

src/pages/blog/[id].astro

---
import { Image } from 'astro:assets';
import { getCollection, render } from 'astro:content';
import { filterAllPosts } from '~/content.config';
import Layout from '~/layouts/Layout.astro';

export async function getStaticPaths() {
  const posts = await getCollection('blog', filterAllPosts);
  return posts.map((post) => ({
    params: { id: post.id },
    props: { post },
  }));
}

const { post } = Astro.props;
const { Content } = await render(post);
---

<Layout title={post.data.title}>
  <Image
    src={post.data.image.src}
    alt={post.data.image.alt || ''}
    class="w-full h-60 object-cover object-bottom"
  />
  <article class="prose mx-auto p-4">
    <h1>{post.data.title}</h1>
    <a href="/">&lt;&lt; Back</a>
    <Content />
  </article>
</Layout>
Enter fullscreen mode Exit fullscreen mode

Image optimization

A src path and alt text can be declared for images in markdown frontmatter, or inline in markdown.

These are passed into the <Image> component which inspects each image, and generates <img> tags with width and height attributes, thereby reducing layout shifts in the browser.

Images are converted to webp format, and stored in dist/_astro with unique (cacheable) names during the build process.

Publishing on Cloudflare Pages

The @astrojs/cloudflare adapter is not needed for static sites.

Cloudflare Pages matches routes with .html files. To avoid trailing slashes, configure the build to generate <route>.html files instead of <route>/index.html.

The _headers file adds cache control headers for immutable content.

Conclusion

Astro v5 is a great choice for a markdown driven blog, as long as you're fine with doing occasional maintenance to update dependencies.

Here are some ideas for future improvements to this starter:

  • Sitemap
  • Menu component for desktop and mobile
  • Nicer fonts
  • Icons for social links

I hope you find this starter useful. Please reach out on X if you have any feedback or suggestions.

Top comments (0)