Zum Inhalt springen

Starlight & Astro Blog Best Practices

Dieser Inhalt ist noch nicht in deiner Sprache verfügbar.

This document details best practices for implementing a blog using Starlight and Astro, based on research into the starlight-blog plugin and Astro’s content collections and routing systems.

The starlight-blog plugin requires the docs collection to be extended with blogSchema. This is done in src/content.config.ts:

import { defineCollection } from 'astro:content';
import { docsLoader } from '@astrojs/starlight/loaders';
import { docsSchema } from '@astrojs/starlight/schema';
import { blogSchema } from 'starlight-blog/schema';
export const collections = {
docs: defineCollection({
loader: docsLoader(),
schema: docsSchema({
extend: (context) => blogSchema(context),
}),
}),
};

2. Starlight-Blog Integration Configuration

Section titled “2. Starlight-Blog Integration Configuration”

The starlight-blog plugin is added as an integration in astro.config.mjs:

import { defineConfig } from 'astro/config';
import starlight from '@astrojs/starlight';
import starlightBlog from 'starlight-blog';
export default defineConfig({
site: 'https://your-site.com',
trailingSlash: 'never',
integrations: [
starlight({
title: "Your Site Title",
defaultLocale: 'root',
locales: {
root: { label: 'Deutsch', lang: 'de', prefixDefaultLocale: false },
en: { label: 'English', lang: 'en' },
},
customCss: ['./src/styles/global.css'],
sidebar: [
// ... sidebar items
],
}),
starlightBlog({
authors: {
'author-id': {
name: 'Author Name',
},
},
metrics: {
readingTime: true,
words: 'rounded',
},
navigation: 'header-end',
prefix: 'blog',
postCount: 10,
recentPostCount: 10,
rss: true,
title: 'Blog',
}),
],
});

Astro Route Matching and Catch-All Segments

Section titled “Astro Route Matching and Catch-All Segments”

The Issue with '[...prefix]/[...page]' Pattern

Section titled “The Issue with '[...prefix]/[...page]' Pattern”

The starlight-blog plugin injects the blog list page route with the pattern '/[...prefix]/[...page]'. In Astro’s route matching:

  • prefix is a catch-all segment
  • page is a catch-all segment

For the blog list page at /blog, the page parameter is undefined. In Astro’s route matching, a pattern with '[...page]' catch-all segment doesn’t correctly match the root blog prefix when page is undefined, which can cause the server to return a 403 or 404 error.

To ensure the blog list page is generated correctly, the starlight-blog plugin’s getBlogStaticPaths() function generates paths with:

params: {
page: index === 0 ? undefined : `${index + 1}`,
prefix: getPathWithLocale(config.prefix, locale),
}

For index 0 (the blog list page), page is undefined, and prefix is blog (or en/blog for English).

Blog posts should be placed in the src/content/docs/blog/ directory with the following structure:

src/content/docs/blog/
├── 2026-07-06-stable-diffusion-bilder-generieren.md
└── ...

Each blog post should have the following frontmatter:

---
title: 'Stable Diffusion - Bilder generieren mit FLUX.2-klein-4B'
description: 'Best practices for implementing a blog using Starlight and Astro, based on research into the starlight-blog plugin and Astro content collections'
date: 2026-07-02
status: verified
date: 2026-07-06
authors:
- michael-wegener
- jane-alesi
---
# Stable Diffusion - Bilder generieren mit FLUX.2-klein-4B
...

When the starlight-blog plugin’s Blog.astro route pattern '/[...prefix]/[...page]' doesn’t correctly match the blog list page when page is undefined, the server may return a 403 or 404 error.

Workaround: Create a custom blog list page component at src/routes/blog/index.astro with explicit /blog route pattern, or update the starlight-blog plugin’s route injection to handle the blog list page correctly.


Open a terminal and run the dev server normally:

Terminal window
cd ~/internal/learn/site
npx astro dev

The server starts on http://localhost:4321/. Files are watched for changes — edits to Markdown files refresh instantly in the browser.

Press Ctrl+C to stop.

Need remote access? Add --host to expose on the local network:

Terminal window
npx astro dev --host

This binds to all interfaces. Check the output for the IP addresses (e.g. http://192.168.0.5:4321/).

Agents (AI coding tools like Cline, Hermes, Little-Coder) execute commands in isolated shells that close after execution. A normal npx astro dev & will be killed when the shell exits.

The server must be a detached background task:

Terminal window
cd ~/internal/learn/site
# Kill any existing instance
pkill -f "astro dev" 2>/dev/null
sleep 1
# Start as a detached background task with log output
(npx astro dev --host > /tmp/astro-dev.log 2>&1) &
disown
# Wait for it to start
sleep 8
# Verify it's running
curl -s -o /dev/null -w "%{http_code}" http://localhost:4321
# Expected: 200
Pattern Problem
npx astro dev & Shell job control sends SIGTERM on exit
nohup npx astro dev & Leaves nohup.out in cwd; sometimes fails in subshells
(npx astro dev > /tmp/astro-dev.log 2>&1) & disown Correct: subshell isolates from parent, disown removes from job table, stdout/stderr logged
Terminal window
# Check if it's still alive
ps aux | grep astro | grep -v grep
# Check logs
tail -f /tmp/astro-dev.log
# Quick health check
curl -s -o /dev/null -w "%{http_code}" http://localhost:4321

The Astro dev server is sensitive to rapid concurrent requests. When verifying pages, space them with delays:

Terminal window
for page in "/learnings/foo" "/blog/bar" "/cheatsheets/baz"; do
status=$(curl -s -o /dev/null -w "%{http_code}" "http://localhost:4321$page")
echo " $page -> $status"
sleep 0.5 # critical: prevent connection-reset from overloading
done

Content Collection Gotcha: support/ Directories

Section titled “Content Collection Gotcha: support/ Directories”

Starlight’s docsLoader() loads all *.md files under src/content/docs/ via glob. Any non-content directory (scripts, fixtures, test data) placed inside will cause schema validation errors:

[InvalidContentEntryDataError] docs → learnings/support/file.md
title: Required

Fix: Move support artifacts outside the content directory entirely:

site/src/content/docs/learnings/ # only .md content files
site/learnings-support/ # scripts, fixtures, test data

Do not rely on _ prefix — the glob pattern **/[^_]*.{ext} matches filenames, not directory names, so _support/file.md still gets picked up.

Error Cause Fix
000 status on curl (agent) Server crashed from concurrent requests Add sleep 0.5 between requests
InvalidContentEntryDataError Non-MD files in content dir Move to learnings-support/
i18n directory does not exist Missing src/content/i18n/ Create it or ignore (non-fatal warning)
Blog list page 404 page undefined in [...page] route See “Known Issues” section above

For blog posts with images, Astro 7.x provides built-in image optimization through the astro:assets module.

  1. Use .mdx files: Use .mdx files (not .md) for JSX components like <Image />
  2. Images in src/ directory: Astro’s image optimization requires images in src/assets/..., not public/assets/...
  3. sharp package required: Must be installed as dev dependency: pnpm add -D sharp
  4. MDX comment syntax: Use {/* comment */} instead of <!-- comment --> in MDX files
  5. No top-level await: Cannot use getImage() with top-level await in MDX files; use <Image src={imageImport} /> instead
---
title: "Blog Post Title"
date: 2026-07-06
---
import { Image } from 'astro:assets';
import natureImage from '../../../assets/images/image-generation/1-nature-landscape.png';
# Blog Post Content
<Image src={natureImage} alt="Nature Landscape" loading="lazy" />

Original PNG files (559-677kB) are optimized to WebP format (26-50kB), achieving 85-95% size reduction:

Original Optimized WebP Reduction
1-nature-landscape.png (580kB) 1-nature-landscape.51SGon9e_2putX.webp (34kB) 94%
2-scifi-cyberpunk.png (631kB) 2-scifi-cyberpunk.JjhCW2vR_Z1taUln.webp (49kB) 92%
3-portrait-realistic.png (588kB) 3-portrait-realistic.BWz-0tfI_m5CB4.webp (26kB) 96%
4-abstract-artistic.png (677kB) 4-abstract-artistic.Cgf-1GlJ_1ATgsX.webp (50kB) 93%
5-food-culinary.png (559kB) 5-food-culinary.BWr7H010_Z20apFE.webp (30kB) 95%

The build generates optimized <img> tags with WebP images:

<img src="/_astro/1-nature-landscape.51SGon9e_2putX.webp" alt="Nature Landscape" loading="lazy" decoding="async" width="512" height="512">