This guide covers everything you need to know about creating and publishing blog posts in Spaceship.
#Creating a New Post
Posts are stored in site/content/posts/ as Markdown (.md) or MDX (.mdx) files.
#File Naming
- Use kebab-case:
my-awesome-post.md - File name becomes the URL slug:
/posts/my-awesome-post - Files starting with
_are ignored
#Basic Post Structure
---
title: 'Your Post Title'
description: 'A brief description for SEO and previews'
pubDate: 2026-02-01
tags: ['astro', 'svelte', 'tutorial']
---
Your content goes here in Markdown or MDX format.
#Frontmatter Options
#Required Fields
- title: Post title (string)
- description: Brief description for SEO and social sharing (string)
- pubDate: Publication date (YYYY-MM-DD format)
#Optional Fields
-
tags: Array of tags (default:
["others"])tags: ['astro', 'svelte', 'tutorial'] -
featured: Mark post as featured on homepage (boolean, default: false)
featured: true -
draft: Mark post as draft (boolean, default: false)
draft: trueDraft posts behavior:
- Development: Visible with a “DRAFT” badge
- Production: Hidden completely
-
updatedDate: Last update date (YYYY-MM-DD format)
updatedDate: 2026-02-05 -
ogImage: Custom Open Graph image path (string)
ogImage: '/images/custom-og.png' -
canonicalURL: Canonical URL for cross-posted content (string)
canonicalURL: 'https://example.com/original-post' -
showCTA: Show/hide CTA block (boolean, default: true)
showCTA: false -
showComments: Show/hide comments section (boolean, default: true)
showComments: false -
series: Group posts into a series (object)
series: id: 'astro-guide' order: 1
#Draft Posts & Publication Date Filtering
Spaceship supports two ways to control post visibility:
#Draft Mode
Mark posts as drafts to hide them in production:
draft: true
Behavior:
- Development (
pnpm dev): Draft posts are visible with a yellow “DRAFT” badge - Production (
pnpm build): Draft posts are completely hidden
Perfect for:
- Work-in-progress posts
- Posts awaiting review
- Unfinished content
#Future-Dated Posts
Posts with future publication dates behave differently based on environment:
pubDate: 2026-03-15 # Future date
Behavior:
- Development (
pnpm dev): All posts visible, regardless of date - Production (
pnpm build): Only posts withpubDate <= todayare shown
Perfect for:
- Scheduling posts for future publication
- Planning content calendar
- Time-sensitive announcements
#Combined Approach
You can use both approaches together:
draft: true # Hide in production
pubDate: 2026-03-15 # Schedule for future
This post will:
- Show in development with “DRAFT” badge
- Remain hidden in production until March 15, 2026
- Automatically publish on that date (if
draft: falseis set)
#Working with Images
#Local Images
Store images in src/assets/ and import them:
---
title: 'Post with Images'
---
import myImage from '@/assets/my-image.png';
import { Image } from 'astro:assets';
<Image src={myImage} alt="Description" />
#Public Images
Place images in public/images/ and reference directly:

#Remote Images

#Image Optimization Tips
- Use WebP or AVIF for better compression
- Optimize before upload - use tools like ImageOptim or Squoosh
- Use descriptive alt text for accessibility and SEO
- Consider lazy loading for performance
#Working with Files
#Downloadable Files
Place files in public/files/ and link to them:
[Download PDF](/files/my-document.pdf)
#Code Files
For syntax-highlighted code snippets:
```javascript
function hello() {
console.log('Hello, World!');
}
```
Or import and display code files using MDX.
#Series Posts
Create a series of related posts:
---
title: 'Getting Started with Astro - Part 1'
series:
id: 'astro-guide'
order: 1
---
---
title: 'Getting Started with Astro - Part 2'
series:
id: 'astro-guide'
order: 2
---
All posts in the same series will show navigation links to other posts.
#Advanced Features
#MDX Support
Use .mdx extension to import components:
---
title: 'Interactive Post'
---
import MyComponent from '@/components/MyComponent.svelte';
<MyComponent client:load />
#Custom CTA
The CTA (Call-to-Action) block at the end of posts is customizable via site/cta.md. To disable it for a specific post:
showCTA: false
#Comments
Comments (via Giscus) can be disabled per post:
showComments: false
#SEO Best Practices
- Write descriptive titles (50-60 characters)
- Craft compelling descriptions (150-160 characters)
- Use relevant tags (3-5 tags per post)
- Add alt text to images
- Use heading hierarchy (H1 → H2 → H3)
- Include internal links to related posts
- Set canonical URLs for cross-posted content
#Publishing Workflow
- Create post in
site/content/posts/ - Add frontmatter with required fields
- Write content in Markdown/MDX
- Test locally with
pnpm dev - Preview at
http://localhost:4321/posts/your-post-slug - Commit and push to deploy
#Quick Reference
#Minimal Post
---
title: 'Hello World'
description: 'My first post'
pubDate: 2026-02-01
---
Hello, World!
#Full-Featured Post
---
title: 'Complete Guide to Astro'
description: 'Everything you need to know about Astro framework'
pubDate: 2026-02-01
updatedDate: 2026-02-05
tags: ['astro', 'tutorial', 'web-development']
featured: true
ogImage: '/images/astro-guide-og.png'
series:
id: 'astro-series'
order: 1
---
Content here...
Pro Tip: Use the VSCode frontmatter snippet to quickly create post templates!