How to Write a Blog Post 📝
Choose your path based on where you're starting from:
🚀 Quick Start
Option 1: Import from dev.to (Easiest!) ⭐
Already have a post on dev.to? Import it with one command:
That's it! The script will: - ✅ Fetch your post from dev.to - ✅ Convert it to the correct format - ✅ Fix date format automatically - ✅ Add "General" category if none found - ✅ Create the file in the right location
Then skip to Step 4: Validate.
Option 2: Write from Scratch
Continue reading below for the full guide.
⚠️ CRITICAL: Date Format Warning
The #1 reason blog builds fail is incorrect date format.
# ✅ CORRECT - Unquoted date (date object)
date: 2026-01-27
# ❌ WRONG - Quoted date (string) - BUILD WILL FAIL!
date: "2026-01-27"
date: '2026-01-27'
# ❌ WRONG - Other formats
date: "January 27, 2026"
date: 27-01-2026
Why it matters: MkDocs Material blog plugin requires a date object, not a string. Quotes make it a string.
How to avoid: Use our validation script (see below) or import from dev.to.
📝 Writing from Scratch
1. File Naming Convention
Blog posts must follow this pattern:
Examples:
- 2026-01-23-getting-started-android.md
- 2026-02-15-firebase-tips-tricks.md
- 2026-03-10-cloud-run-tutorial.md
Location: docs/blog/posts/
2. Post Template
Create your file with this template:
---
draft: false
date: 2026-01-27
authors:
- your_author_id
categories:
- Android
---
# Your Awesome Post Title
A brief introduction (1-2 sentences that hook the reader).
<!-- more -->
## Introduction
Your content starts here...
## Main Content
Add your sections...
## Conclusion
Wrap it up!
---
*About the author: Brief bio or link*
3. Front Matter Fields Explained
draft: false # true = hidden, false = published
date: 2026-01-27 # ⚠️ UNQUOTED! Format: YYYY-MM-DD
authors: # From .authors.yml (see below)
- your_id
categories: # Pick from list below
- Android # At least one required
- Firebase # Can have multiple
Categories Available: - Android - Firebase - Google Cloud - Flutter - AI & ML - Web - Maps - Ads - Workspace - General (default if none specified)
4. The <!-- more --> Separator (Important!)
All blog posts must include the <!-- more --> separator.
This separator: - Creates an excerpt for the blog list page - Adds a "Continue reading" button - Improves user experience
# Your Post Title
Your introduction paragraph (1-3 sentences).
<!-- more -->
## Full Content Starts Here
The rest of your post...
What happens without it: - ❌ The entire post shows on the blog list page - ❌ No "Continue reading" button appears - ❌ Poor user experience
Note: The pre-commit hook will warn you if it's missing!
5. Adding Yourself as an Author
Edit docs/blog/.authors.yml:
your_github_username:
name: Your Full Name
description: GDE for Android
avatar: https://github.com/your_username.png
url: https://your-website.com
✅ 4. Validate Before Submitting (Recommended)
Always run the validator before committing:
# Validate all posts
./scripts/validate-blog-posts.sh
# Validate specific post
./scripts/validate-blog-posts.sh docs/blog/posts/2026-01-27-my-post.md
# Auto-fix common issues (date format, missing categories)
./scripts/validate-blog-posts.sh --fix
What it checks:
- ✅ Date format (quoted vs. unquoted)
- ✅ Categories format (string vs. list)
- ✅ Required fields (date, authors, categories)
- ✅ Author exists in .authors.yml
- ✅ Categories are valid
- 🔧 Auto-fix: Removes quotes from dates
- 🔧 Auto-fix: Converts categories string → list
- 🔧 Auto-fix: Adds "General" if no categories
Example output:
Validating: 2026-01-27-my-post.md
✓ Date format: ✓ (unquoted date object)
✓ Authors field: ✓
✓ Categories field: ✓ (2 categories)
Status: PASS
🔒 5. Enforce Quality with Git Hooks (Recommended)
Automatic validation on every commit!
Install the pre-commit hook to automatically validate blog posts before committing:
What it does: - ✅ Runs automatically before every commit - ✅ Validates all blog posts in the commit - ✅ Blocks commits with errors (date format, categories, etc.) - ✅ Shows clear error messages - ✅ Suggests fixes
Example:
git add docs/blog/posts/2026-01-27-my-post.md
git commit -m "Add blog post"
# Hook runs automatically:
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
GDE Americas Hub - Pre-commit Validation
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
✗ Date is quoted (string) - this will BREAK the build!
Current: date: "2026-01-27"
Should be: date: 2026-01-27
✗ COMMIT BLOCKED
Fix with: ./scripts/validate-blog-posts.sh --fix
Bypass the hook (use sparingly!):
6. Preview Locally
# Install dependencies (first time only)
pip install -r requirements.txt
# Start local server
mkdocs serve
# Visit: http://127.0.0.1:8000/gde-americas-hub/blog/
Your post will appear in the blog list!
7. Submit Your Post
# Create a branch
git checkout -b blog/my-awesome-post
# Add your files
git add docs/blog/posts/2026-01-27-my-post.md
git add docs/blog/.authors.yml # If you added yourself
# Commit
git commit -m "Add blog post: My Awesome Post"
# Push
git push origin blog/my-awesome-post
# Open PR on GitHub
📊 Complete Workflow (Visual)
┌─────────────────────────────────────────────────────┐
│ ONE-TIME SETUP: │
│ ./scripts/install-git-hooks.sh │
│ (Automatic validation on every commit) │
│ │
│ ────────────────────────────────────────────── │
│ │
│ Have post on dev.to? │
│ ├─ YES → ./scripts/import-from-devto.sh URL │
│ └─ NO → Write from scratch (template above) │
│ │
│ ↓ │
│ Add <!-- more --> separator after intro │
│ │
│ ↓ │
│ Run validator (optional, hook does this): │
│ ./scripts/validate-blog-posts.sh --fix │
│ │
│ ↓ │
│ Preview locally: │
│ mkdocs serve │
│ │
│ ↓ │
│ git commit (hook validates automatically!) │
│ │
│ ↓ │
│ git push │
│ │
│ ↓ │
│ Open PR → Get reviewed → Merge → Published! 🎉 │
└─────────────────────────────────────────────────────┘
💡 Best Practices
- Clear titles - Descriptive and specific
- Code examples - Working, tested code snippets
- Images - Place in
/docs/assets/images/blog/ - Links - Reference official docs
- Proofread - Check spelling and grammar
- Use
<!-- more -->- Separates excerpt from full content - Validate first - Always run the validator before submitting
🆘 Common Issues & Solutions
Issue 1: Build fails with "expected date object"
Cause: Date is quoted (string)
Solution:
This removes quotes automatically.
Issue 2: Build fails with "Expected a list of items, but a string was given"
Cause: Categories is a STRING instead of a LIST
Example of problem:
Solution:
This converts to proper list format:
Issue 3: "Author not found"
Cause: Author ID not in .authors.yml
Solution: Add yourself to .authors.yml first
Issue 3: No categories
Cause: Forgot to add categories
Solution:
This adds "General" category automatically.
Issue 4: Post not appearing
Cause: draft: true in frontmatter
Solution: Change to draft: false
Issue 5: No "Continue reading" button
Cause: Missing <!-- more --> separator
Solution: Add the separator after your introduction:
The pre-commit hook will warn you about this!
📁 File Structure
docs/blog/
├── .authors.yml # Author definitions
├── index.md # Blog homepage
├── HOW_TO_POST.md # This guide
└── posts/
├── 2026-01-23-welcome.md
└── your-posts-here.md
🛠️ Helper Scripts
| Script | Purpose |
|---|---|
import-from-devto.sh |
Import post from dev.to |
validate-blog-posts.sh |
Validate frontmatter |
validate-blog-posts.sh --fix |
Auto-fix common issues |
install-git-hooks.sh |
Install pre-commit validation hook |
📚 Resources
- Example post: See
posts/2026-01-23-welcome.md - Contributing guide: CONTRIBUTING.md
- MkDocs Material blog: Official docs
❓ Questions?
- Check CONTRIBUTING.md
- Open an issue on GitHub
- Ask in community forums
Happy blogging! 🎉
Remember: Validate before submitting! It saves everyone time.