DEV Community

Cover image for Astro v5 Blog starter
jldec
jldec

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

2

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.

Heroku

Simplify your DevOps and maximize your time.

Since 2007, Heroku has been the go-to platform for developers as it monitors uptime, performance, and infrastructure concerns, allowing you to focus on writing code.

Learn More

Top comments (0)

Cloudinary image

Optimize, customize, deliver, manage and analyze your images.

Remove background in all your web images at the same time, use outpainting to expand images with matching content, remove objects via open-set object detection and fill, recolor, crop, resize... Discover these and hundreds more ways to manage your web images and videos on a scale.

Learn more