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)