TL;DR
AstroWind is the best free boilerplate for static content sites — Lighthouse 100, 0KB JavaScript, and excellent Markdown support. Ghost is the best choice for publication businesses with newsletters and member paywalls. Next.js + Sanity is best when non-technical editors need a polished CMS. Payload CMS is the most powerful self-hosted headless CMS for developer-driven content platforms.
Content Platforms vs SaaS
Building a content platform — a blog, documentation site, knowledge base, or media publication — has different requirements from a SaaS application:
- Performance is non-negotiable — Google's Core Web Vitals affect search ranking directly
- SEO from day one — Meta tags, structured data, sitemaps, canonical URLs must be built in
- Content editing — Non-technical editors need a usable interface
- Static generation — Most content doesn't change between requests; build-time rendering is optimal
The right choice depends on how much dynamic content you need, who edits content, and whether you need membership or subscription features.
Quick Comparison
| Starter | CMS | Dynamic | SEO | PageSpeed | Newsletter | Paywall | Best For |
|---|---|---|---|---|---|---|---|
| AstroWind | MDX files | ❌ | ✅ | 98-100 | ❌ | ❌ | Static content sites |
| Ghost | Built-in | Auth | ✅ | 90-95 | ✅ | ✅ | Publications, newsletters |
| Next.js + Contentlayer | MDX files | ✅ | ✅ | 90-98 | ❌ | ❌ | Content + app hybrid |
| Next.js + Sanity | Sanity Studio | ✅ | ✅ | 90-98 | ❌ | ❌ | Editor-friendly CMS |
| Payload CMS | Built-in | ✅ | ✅ | 80-95 | ❌ | ❌ | Headless CMS platform |
The Starters
AstroWind — Best Static Content
Price: Free | Creator: onWidget | GitHub Stars: 4k+
The gold standard for content sites. Astro's partial hydration delivers 0KB JavaScript for static pages, with JavaScript hydrated only where interactive components need it. Tailwind CSS, MDX blog posts, SEO components, sitemap generation, and deployment configs for Netlify/Vercel.
Performance: PageSpeed 98-100. Lighthouse perfect score is achievable without optimization work.
git clone https://github.com/onwidget/astrowind
cd astrowind && npm install && npm run dev
# Localhost:4321
Content structure:
src/content/
├── blog/
│ ├── getting-started.mdx
│ └── advanced-patterns.mdx
└── post/
└── featured-post.mdx
# Each MDX file has frontmatter:
---
title: "Building with Astro"
description: "How Astro's Islands Architecture delivers..."
publishDate: 2026-02-15
author: "Team"
tags: ["astro", "performance"]
image: /images/post-hero.png
---
Choose if: Building a blog, documentation site, or marketing site where performance and SEO are the primary requirements.
Ghost — Best Publication Platform
Price: Free (self-hosted) / $9-$199/month (Ghost Pro) | Creator: Ghost Foundation | GitHub Stars: 48k+
Purpose-built content platform. Rich block editor (Koenig), newsletter subscriptions, member paywall with Stripe billing, built-in SEO (structured data, sitemaps, Twitter cards, Open Graph), and excellent RSS/JSON Feed support.
# Self-hosted installation
npm install -g ghost-cli
mkdir my-publication && cd my-publication
ghost install # Requires Ubuntu, nginx, MySQL
# Or use Ghost Pro (managed hosting)
# From $9/month — no server required
Ghost membership tiers:
// members.js — multiple pricing tiers
{
tiers: [
{
name: "Free",
type: "free",
description: "Public content only",
},
{
name: "Supporter",
type: "paid",
monthly_price: 5,
annual_price: 50,
description: "All content + weekly newsletter",
},
{
name: "Founding Member",
type: "paid",
monthly_price: 15,
annual_price: 150,
description: "All content + Discord + monthly call",
}
]
}
Choose if: Building a media publication, newsletter business, or paywalled content platform — Ghost is the best product for this specific use case.
Next.js + Contentlayer — Best Hybrid App + Content
Price: Free | Creator: Various
Content from MDX files + Next.js App Router + Contentlayer (type-safe content). Best when you need both static content marketing AND dynamic SaaS features — authentication, personalization, comments, API routes.
// contentlayer.config.ts — typed MDX content definition
import { defineDocument } from 'contentlayer/source-files';
export const Post = defineDocument(() => ({
name: 'Post',
filePathPattern: 'blog/**/*.mdx',
fields: {
title: { type: 'string', required: true },
date: { type: 'date', required: true },
description: { type: 'string', required: true },
tags: { type: 'list', of: { type: 'string' } },
draft: { type: 'boolean', default: false },
},
computedFields: {
slug: {
type: 'string',
resolve: (doc) => doc._raw.flattenedPath.replace('blog/', ''),
},
url: {
type: 'string',
resolve: (doc) => `/blog/${doc._raw.flattenedPath.replace('blog/', '')}`,
},
},
}));
// app/blog/[slug]/page.tsx — type-safe blog post
import { allPosts } from 'contentlayer/generated';
import { useMDXComponent } from 'next-contentlayer/hooks';
export async function generateStaticParams() {
return allPosts.map((post) => ({ slug: post.slug }));
}
export default function PostPage({ params }: { params: { slug: string } }) {
const post = allPosts.find((p) => p.slug === params.slug);
const MDXContent = useMDXComponent(post!.body.code);
return (
<article className="prose dark:prose-invert max-w-3xl mx-auto">
<h1>{post!.title}</h1>
<MDXContent />
</article>
);
}
Choose if: You need both content publishing and authenticated SaaS features in one Next.js application — not a separate deployment.
Next.js + Sanity — Best Editor Experience
Price: Free (self-hosted Sanity Studio) / Sanity Cloud is generous | Creator: Sanity.io | Stars: Sanity Studio 5k+
Sanity's Studio provides the best headless CMS editing experience. Real-time collaborative editing, custom content models, image CDN with transformation URL parameters, and a React-based studio that you can customize completely.
// sanity/schemas/post.ts — custom content model
import { defineType, defineField } from 'sanity';
export default defineType({
name: 'post',
title: 'Blog Post',
type: 'document',
fields: [
defineField({ name: 'title', type: 'string', validation: Rule => Rule.required() }),
defineField({ name: 'slug', type: 'slug', options: { source: 'title' } }),
defineField({
name: 'body',
type: 'array',
of: [
{ type: 'block' },
{ type: 'image', options: { hotspot: true } },
{ type: 'code' }, // Syntax highlighted code blocks
],
}),
defineField({ name: 'publishedAt', type: 'datetime' }),
defineField({
name: 'categories',
type: 'array',
of: [{ type: 'reference', to: { type: 'category' } }],
}),
],
});
// app/blog/[slug]/page.tsx — Sanity content in Next.js
import { client } from '@/lib/sanity.client';
import { PortableText } from '@portabletext/react';
const POST_QUERY = `*[_type == "post" && slug.current == $slug][0] {
title, body, publishedAt,
"categories": categories[]->{title},
"author": author->{name, image}
}`;
export default async function PostPage({ params }: { params: { slug: string } }) {
const post = await client.fetch(POST_QUERY, { slug: params.slug });
return (
<article>
<h1>{post.title}</h1>
<PortableText value={post.body} />
</article>
);
}
Choose if: Non-technical editors are creating content regularly and need a polished, intuitive editing interface.
Payload CMS — Best Headless CMS Platform
Price: Free (self-hosted) / Cloud tiers | Creator: Payload team | GitHub Stars: 30k+
The most powerful self-hosted headless CMS built with TypeScript. Define your content model in TypeScript; Payload generates the admin UI, REST API, and GraphQL API automatically. Version 3.0 integrates directly into Next.js as a server-side package — no separate deployment.
// payload.config.ts — content model definition
import { buildConfig } from 'payload/config';
import { Posts } from './collections/Posts';
import { Media } from './collections/Media';
export default buildConfig({
collections: [Posts, Media],
admin: { user: Users.slug },
db: postgresAdapter({ pool: { connectionString: process.env.DATABASE_URL } }),
editor: lexicalEditor({}),
});
Choose if: You're building a content platform where you need the CMS to be your product — custom fields, custom workflows, white-label admin.
Content + SaaS: The Hybrid Architecture
For a SaaS product with a content marketing blog, you don't have to choose one approach:
your-saas.com/ → Next.js app (auth, dashboard, API)
your-saas.com/blog → Astro or Next.js + MDX
your-saas.com/docs → Starlight or Docusaurus
# OR: subdomain separation
blog.your-saas.com → Separate Astro/Ghost deployment
docs.your-saas.com → Separate Docusaurus deployment
app.your-saas.com → Main Next.js SaaS app
The subdomain approach lets you optimize each independently: Astro for the marketing blog (0KB JS, PageSpeed 100), Next.js for the application (React, auth, real-time features), and Hugo/Docusaurus for documentation (Markdown-native, fast search).
SEO Checklist for Content Platforms
Every content platform needs these from day one:
// app/blog/[slug]/page.tsx — metadata generation
export async function generateMetadata({ params }: Props): Promise<Metadata> {
const post = await getPost(params.slug);
return {
title: post.title,
description: post.description,
openGraph: {
title: post.title,
description: post.description,
url: `https://yoursite.com/blog/${post.slug}`,
images: [{ url: post.ogImage, width: 1200, height: 630 }],
},
twitter: {
card: 'summary_large_image',
title: post.title,
description: post.description,
},
};
}
Technical SEO requirements:
-
sitemap.xmlauto-generated from content -
robots.txtwith correct rules - Canonical URLs on all pages
- Structured data (Article, BreadcrumbList)
- Open Graph and Twitter Card meta tags
- RSS and/or JSON Feed
- 301 redirects for any URL changes
CMS Selection Framework: Choosing the Right Platform
The decision between content platform options depends on who creates the content, how often it changes, and what technical capabilities are required.
MDX + Next.js (Velite/Contentlayer) is the right choice when: engineers own most of the content, you want content and code in the same Git repository, content changes are infrequent enough that a deploy pipeline is acceptable, and you need React components embedded in content (interactive demos, custom callouts, API playground widgets). The developer experience is excellent; the non-developer experience is terrible. Never choose this when non-technical content editors need to publish frequently.
Sanity is the right choice when: content editors need a polished writing experience, content types are complex and relational (a post has an author, the author has a profile, the profile has social links, etc.), the content model will evolve over time (Sanity's schema-as-code means content model changes are version-controlled and reversible), and you need real-time collaborative editing for content teams. The free tier (3 users, 10K documents) handles most small publications.
Ghost is the right choice when: you're building a publishing business where the content platform is the product (not a marketing site for a SaaS product). Ghost's membership, newsletter, and paid subscription features are purpose-built for media businesses. If you're running a newsletter-backed blog or building a Substack alternative, Ghost is faster to get right than any custom implementation.
Payload CMS is the right choice when: you need a headless CMS that is itself a TypeScript product you control and customize. Payload 3.0's Next.js integration means the admin UI runs inside your Next.js application — no separate deployment. The content model is TypeScript code, generating typed API responses automatically. For a product where the CMS admin is part of your product (a multi-tenant publishing platform, a white-label content service), Payload is the only option that gives you this level of control.
Content Strategy for SaaS Products
For a SaaS product with a content marketing component, the technical platform choice is less important than the content strategy. Three models work:
Product-adjacent content: Write about the problems your product solves, not about the product itself. A project management tool's blog should cover engineering team dynamics, sprint planning, and technical decision-making — not "how to use our app." This content attracts potential customers through search and provides value regardless of whether they buy.
Comparison content: Articles like "ShipFast vs Supastarter" or "Stripe vs Paddle for SaaS billing" rank for high-intent search queries. Readers making tool selection decisions are often ready to buy something. If your product is in the comparison, you're in front of warm prospects at exactly the right moment.
Developer documentation as SEO: Well-written technical documentation with examples ranks well for specific developer queries. If you build an API, a page that explains "how to use our API with Next.js" will rank for "[your API] Next.js integration" — bringing in developers actively working on integrations. This is the content that StarterPick's boilerplate-specific guides serve.
For a deeper look at how newsletter products differ from content marketing blogs, the newsletter and email product comparison covers Ghost and Beehiiv. For your SaaS boilerplate that will host the blog, the best full-stack TypeScript boilerplates guide covers which starters have the best MDX integration.
Image Optimization Strategy for Content Platforms
Images are the primary cause of poor Core Web Vitals on content sites. The implementation varies by platform:
Astro uses astro:assets for build-time image optimization:
import { Image } from 'astro:assets';
import heroImage from '../assets/post-hero.jpg';
// Outputs WebP + original format, with proper width/height attributes
<Image src={heroImage} alt="Post hero" width={1200} height={630} format="webp" />
// For dynamic paths (not recommended — Astro can't optimize at build time)
<img src="/images/dynamic.jpg" alt="Dynamic" width={800} height={400} loading="lazy" />
Build-time optimization outputs correctly sized images in modern formats. The width and height attributes prevent Cumulative Layout Shift — images without explicit dimensions shift the page as they load, hurting both CLS score and reader experience.
Next.js + Sanity offloads image optimization to Sanity's CDN:
import imageUrlBuilder from '@sanity/image-url';
const builder = imageUrlBuilder(client);
// Sanity CDN resizes and converts to WebP automatically
function urlFor(source: SanityImageSource) {
return builder.image(source)
.auto('format') // serve WebP to browsers that support it
.fit('max')
.quality(80);
}
// Use in components
<img
src={urlFor(post.mainImage).width(1200).height(630).url()}
alt={post.mainImage.alt}
width={1200}
height={630}
loading="lazy"
/>
Sanity's image CDN is included in the free tier (up to 200K transformations/month). For a content platform with moderate traffic, this never costs anything. The URL parameter approach means you can request different sizes for different breakpoints without storing multiple image variants.
Search and Discovery for Content Platforms
Static content sites need search. The two practical approaches for MDX-based platforms in 2026:
Pagefind (recommended for Astro/Hugo): Client-side search index built from static HTML at build time. Zero ongoing cost, no external service, works offline. The index is ~1KB per page — a 500-page site adds ~500KB download, but this loads lazily after the page is interactive.
# Pagefind integrates in one line for Astro
npx pagefind --site dist
# Generates /pagefind/ search index from your built HTML
Algolia DocSearch (for documentation sites): Free for open source documentation, $50-$500/month for commercial content. The best search experience available — typo-tolerance, contextual results, faceted filtering. Worth the cost when search is a primary navigation mode (developer documentation, large knowledge bases).
For Ghost: Ghost includes native search powered by FlexSearch. No configuration needed — it's built into every Ghost instance.
For a blog with under 200 posts, Pagefind is the right choice: it works, it's free, and it adds search to your existing static deployment without architectural changes. Move to Algolia when search quality becomes a product priority.
Comment Systems: The Trade-off Between Engagement and Maintenance
Adding comments to a content platform introduces complexity: spam moderation, email notifications, user accounts, and a database to maintain. The decision depends on how much engagement you want to manage.
Giscus: Free, backed by GitHub Discussions. Readers authenticate with GitHub — ideal for developer content where your audience already has GitHub accounts. Comments are stored in your GitHub repo's Discussions tab. Zero ongoing cost, excellent spam resistance (GitHub accounts are real), built-in threading.
<!-- Add to your blog post layout -->
<script src="https://giscus.app/client.js"
data-repo="yourname/your-repo"
data-repo-id="R_kgDOHxxxxxx"
data-category="Blog Comments"
data-category-id="DIC_kwDOHxxxxxx"
data-mapping="pathname"
data-strict="0"
data-reactions-enabled="1"
data-theme="preferred_color_scheme"
async>
</script>
No comments (the default for most content sites): The majority of content platforms don't have comments — engagement happens on Twitter/X, LinkedIn, or Hacker News instead. For a new publication, not having comments is often the right choice: the maintenance overhead isn't justified until you have enough traffic to generate meaningful discussion. Add comments when you have an audience that will use them, not preemptively.
RSS and Content Distribution
Every serious content platform should have an RSS feed. RSS readers (Feedly, NetNewsWire, Reeder) still drive a meaningful portion of returning reader traffic for technical content, and RSS is the standard integration for podcast apps and content aggregators.
For Astro, RSS is a first-party package:
// src/pages/rss.xml.ts
import rss from '@astrojs/rss';
import { getCollection } from 'astro:content';
export async function GET(context) {
const posts = await getCollection('blog');
return rss({
title: 'Your Blog',
description: 'Technical writing about...',
site: context.site,
items: posts.map((post) => ({
title: post.data.title,
pubDate: post.data.date,
description: post.data.description,
link: `/blog/${post.slug}/`,
})),
});
}
Ghost generates RSS automatically. For Next.js + Contentlayer, the feed npm package produces RSS, Atom, and JSON Feed simultaneously from your posts array.
JSON Feed is worth adding alongside RSS — it's the modern format preferred by many aggregators and easier to consume programmatically than XML.
Having both RSS and JSON Feed means your content is automatically compatible with any reader, newsletter tool that imports feeds, or aggregator that curates developer content. Indie Hackers, Hacker News, and many developer-focused link aggregators accept RSS submissions — a properly formatted feed makes your content discoverable to these audiences without additional distribution work. The revival of RSS readership driven by social media fatigue has made feed support more valuable than it appeared in the mid-2010s — independent content platforms with well-structured feeds are gaining audience from readers who prefer curated, algorithm-free reading over social media discovery.
Compare content platform and SaaS boilerplates in the StarterPick directory.
See our review of Ghost for publications vs newsletter-first platforms.
Explore portfolio site boilerplates for the personal site use case with similar needs.