Blog Plugin Configuration
Complete guide to configuring the Docusaurus blog plugin for optimal blogging experience.
Overview
The @docusaurus/plugin-content-blog plugin provides the blog feature for Docusaurus. When using @docusaurus/preset-classic, the blog plugin is included by default and can be configured through preset options.
Current Configuration
Our blog is configured in docusaurus.config.ts with the following settings:
blog: {
blogTitle: 'Pacing Agency Blog',
blogDescription: 'Digital marketing insights, tech stack tips, and industry expertise from Pacing Agency.',
blogSidebarTitle: 'Recent posts',
blogSidebarCount: 10,
postsPerPage: 10,
showReadingTime: true,
feedOptions: {
type: ['rss', 'atom'],
xslt: true,
title: 'Pacing Agency Blog',
description: 'Digital marketing insights, tech stack tips, and industry expertise from Pacing Agency.',
copyright: `Copyright © ${new Date().getFullYear()} Pacing Agency`,
},
editUrl: 'https://github.com/Pacing-Agency/techstackdocs/tree/main/docusaurus/',
onInlineTags: 'warn',
onInlineAuthors: 'warn',
onUntruncatedBlogPosts: 'warn',
}
Key Configuration Options
Basic Settings
| Option | Value | Description |
|---|---|---|
blogTitle | 'Pacing Agency Blog' | Blog page title for SEO |
blogDescription | Marketing insights... | Meta description for SEO |
routeBasePath | 'blog' (default) | URL route for blog section |
postsPerPage | 10 | Number of posts per listing page |
showReadingTime | true | Show estimated reading time |
Sidebar Configuration
| Option | Value | Description |
|---|---|---|
blogSidebarTitle | 'Recent posts' | Title of blog sidebar |
blogSidebarCount | 10 | Number of posts to show in sidebar |
Options: Use 'ALL' to show all posts, or 0 to disable the sidebar entirely.
Feed Configuration
RSS and Atom feeds are automatically generated for blog subscribers:
feedOptions: {
type: ['rss', 'atom'], // Generate both RSS and Atom feeds
xslt: true, // Style feeds for browser viewing
title: 'Pacing Agency Blog',
description: 'Digital marketing insights...',
copyright: `Copyright © ${new Date().getFullYear()} Pacing Agency`,
}
Feed URLs:
- RSS:
/blog/rss.xml - Atom:
/blog/atom.xml
Note: Feed generation only works in production builds, not in development.
Best Practices Enforcement
onInlineTags: 'warn', // Warn when tags not in tags.yml
onInlineAuthors: 'warn', // Warn when authors not in authors.yml
onUntruncatedBlogPosts: 'warn', // Warn when missing <!-- truncate -->
Options: 'ignore' | 'log' | 'warn' | 'throw'
Use 'throw' to enforce strict compliance (build will fail on violations).
File Structure
docusaurus/blog/
├── authors.yml # Author definitions
├── tags.yml # Tag definitions
├── YYYY-MM-DD-post-slug.mdx # Blog posts
└── .sync-manifest.json # Webflow sync tracking (gitignored)
docusaurus/static/img/blog/ # Blog images
Authors Configuration
Define authors in blog/authors.yml:
ben-power:
name: Ben Power
title: Founder, Pacing Agency
url: https://pacing.agency
image_url: https://pacing.agency/img/ben-power.jpg
socials:
github: benpower
x: benpower
linkedin: ben-power
pacing-agency:
name: Pacing Agency
title: Digital Marketing Agency
url: https://pacing.agency
image_url: https://pacing.agency/img/logo.svg
Supported social platforms:
github,x,twitter,linkedin,stackoverflowinstagram,bluesky,mastodon,threadstwitch,youtube,email
Use handles for pre-defined platforms:
socials:
github: slorber # Just the handle
x: sebastienlorber # Just the handle
linkedin: sebastienlorber # Just the handle
Tags Configuration
Define tags in blog/tags.yml:
channel-deep-dives:
label: 'Channel Deep Dives'
permalink: '/channel-deep-dives'
description: 'In-depth analysis of marketing channels and strategies'
data-analytics:
label: 'Data & Analytics'
permalink: '/data-analytics'
description: 'Technical insights on tracking, analytics, and data accuracy'
website-ux:
label: 'Website & UX'
permalink: '/website-ux'
description: 'Website design, development, and user experience topics'
Post Frontmatter
Blog posts support extensive frontmatter metadata:
---
slug: my-blog-post # URL slug (optional)
title: My Awesome Blog Post # Required
date: 2025-01-15 # Required (YYYY-MM-DD)
authors: # Use author keys from authors.yml
- ben-power
- cam-bowen
tags: # Use tag keys from tags.yml
- data-analytics
- website-ux
description: > # SEO meta description
Learn how to optimize your website analytics
for better insights and decisions.
image: /img/blog/featured.jpg # Featured/social image
hide_table_of_contents: false # Show TOC (default: false)
toc_min_heading_level: 2 # TOC min level (2-6)
toc_max_heading_level: 3 # TOC max level (2-6)
keywords: # SEO keywords
- analytics
- optimization
- tracking
draft: false # Draft posts (dev only)
unlisted: false # Hidden but accessible via direct link
---
Advanced Frontmatter
---
title_meta: Custom SEO Title # Override <title> for SEO
sidebar_label: Short Label # Custom sidebar label
last_update: # Override last update info
date: 2025-01-20
author: ben-power
---
URL Structure
- Blog index:
/blog - Blog post:
/blog/my-blog-post - Tag pages:
/blog/tags/data-analytics - Archive:
/blog/archive - Author pages:
/blog/authors/ben-power - RSS feed:
/blog/rss.xml - Atom feed:
/blog/atom.xml
Advanced Configuration
Custom Reading Time
readingTime: ({content, frontMatter, defaultReadingTime}) => {
// Custom reading time calculation
return defaultReadingTime({content, options: {wordsPerMinute: 250}});
}
Custom Feed Items
feedOptions: {
createFeedItems: async (params) => {
const {blogPosts, defaultCreateFeedItems, ...rest} = params;
return defaultCreateFeedItems({
// Keep only 10 most recent posts in feed
blogPosts: blogPosts.filter((item, index) => index < 10),
...rest,
});
},
}
Process Blog Posts
processBlogPosts: async ({blogPosts}) => {
// Filter, modify, or transform blog posts
return blogPosts.filter(post => !post.metadata.frontMatter.draft);
}
Custom Edit URL
editUrl: ({locale, blogDirPath, blogPath, permalink}) => {
return `https://github.com/org/repo/edit/main/website/${blogDirPath}/${blogPath}`;
}
MDX Plugins
Add Remark and Rehype plugins for extended Markdown functionality:
blog: {
remarkPlugins: [
require('remark-math'),
require('remark-gfm'),
],
rehypePlugins: [
require('rehype-katex'),
],
}
Localization
For multi-language blogs, translations are stored in:
website/i18n/[locale]/docusaurus-plugin-content-blog/
├── authors.yml # Translated authors
├── tags.yml # Translated tags
├── first-post.md # Translated posts
└── options.json # Translated plugin options
Best Practices
- Always use
<!-- truncate -->in posts to control preview length - Define authors in
authors.ymlrather than inline in frontmatter - Define tags in
tags.ymlfor consistency across posts - Use descriptive slugs for SEO (avoid generic slugs like
post-1) - Add featured images for better social sharing (
imagefrontmatter) - Write good descriptions for SEO and social cards
- Use proper heading hierarchy (H2 → H3 → H4)
- Test feeds in production (feeds don't work in dev mode)
- Optimize images before uploading (use WebP/AVIF formats)
- Cross-link content to improve discoverability
Webflow Sync Integration
Blog posts are automatically synced from Webflow CMS using our custom sync script:
./scripts/resources/webflow/scripts/run-sync.sh
See the Webflow documentation in /scripts/resources/webflow/README.md for sync script details.
Related Documentation
- Blog Functionality Guide - Complete guide to blogging features
- Webflow Sync Scripts - Webflow API and automation
- Docusaurus Blog Plugin - Official documentation
Troubleshooting
Feed not generating
Cause: Feeds only work in production builds
Solution: Run npm run build and check build/blog/rss.xml
Author not showing
Cause: Author key doesn't match authors.yml
Solution: Check spelling and ensure author exists in authors.yml
Tag page 404
Cause: Tag permalink doesn't match tag definition
Solution: Verify tag key in tags.yml matches frontmatter
Post not truncating
Cause: Missing <!-- truncate --> marker
Solution: Add truncate marker after intro paragraph
Reading time incorrect
Cause: Default calculation based on word count
Solution: Customize with readingTime function in config