Headless CMS Integration
Learn how to integrate headless CMS platforms with your Astro site while maintaining the same content layer API and all existing features.
Master advanced CMS concepts including hierarchical content, headless CMS integration, and custom implementations
Take your CMS implementation to the next level with advanced patterns, integrations, and customizations.
Create complex content structures with unlimited nesting:
/docs/ # Level 1: Root Hub
├── /docs/getting-started/ # Level 2: Sub-hub
│ ├── /docs/getting-started/installation/
│ └── /docs/getting-started/quickstart/
├── /docs/api/ # Level 2: Another sub-hub
│ ├── /docs/api/authentication/
│ ├── /docs/api/endpoints/
│ └── /docs/api/webhooks/
│ ├── /docs/api/webhooks/creating/ # Level 3: Deep nesting
│ └── /docs/api/webhooks/testing/
└── /docs/guides/
└── /docs/guides/best-practices/
Define relationships via folder structure:
# Folder-based hierarchy (NO parent field needed):
src/content/cms/api/index.md # Hub page
src/content/cms/api/authentication.md # Child (URL: /cms/api/authentication/)
src/content/cms/api/endpoints.md # Child (URL: /cms/api/endpoints/)
Frontmatter:
---
title: API Authentication
order: 1 # Optional: Sort order within folder
---
Folder index pages (index.md) automatically become archive pages when the folder contains other files. No manual configuration needed!
/docs/
├── overview (landing page)
├── getting-started/ (hub)
├── api-reference/ (hub)
├── guides/ (hub)
└── troubleshooting/ (hub)
/kb/
├── categories/
│ ├── billing/
│ ├── technical/
│ └── account/
└── search results page
/products/
├── product-a/
│ ├── features/
│ ├── pricing/
│ └── docs/
└── product-b/
├── features/
├── pricing/
└── docs/
The CMS architecture supports seamless WordPress integration:
npm install @astro-cms/wordpress-loader
// src/config/cms.js
export default {
source: 'wordpress',
wordpress: {
endpoint: 'https://your-site.com/wp-json/wp/v2',
cache: true,
ttl: 3600,
},
};
// src/content/config.ts
import { wordPressLoader } from '@astro-cms/wordpress-loader';
const cms = defineCollection({
loader: wordPressLoader(cmsConfig.wordpress),
schema: cmsSchema,
});
WordPress data automatically maps to CMS schema:
The same pattern works for:
sanityLoader()contentfulLoader()strapiLoader()Run multiple content sources simultaneously:
const cmsLocal = defineCollection({
loader: glob({ pattern: '**/*.md' }),
});
const cmsWordPress = defineCollection({
loader: wordPressLoader({ endpoint: '...' }),
});
// Use both!
export const collections = {
cms: mergeSources([cmsLocal, cmsWordPress]),
};
Use Cases:
Create your own archive layout:
---
// src/components/cms/archives/CustomArchive.astro
import { calculateReadingTime } from '../../../lib/cms.js';
export interface Props {
items: any[];
// ... other props
}
---
<div class="custom-archive">
{items.map(item => (
<!-- Your custom layout -->
))}
</div>
Register in routing:
{archiveLayout === 'custom' && <CustomArchive items={paginatedChildren} />}
Follow the same pattern for single pages:
// src/components/cms/singles/CustomLayout.astro
Create reusable components:
// src/components/ui/CustomComponent.astro
export interface Props { // Define props }
<!-- Component markup -->
Use in markdown:
{/_ custom
prop1: "value"
prop2: "value"
_/}
Override default routing:
// src/pages/custom/[category]/[slug].astro
export async function getStaticPaths() {
// Custom path generation
}
Set up redirects for legacy URLs:
// astro.config.mjs
export default defineConfig({
redirects: {
'/old-path': '/new-path',
'/blog/[...slug]': '/cms/[...slug]',
},
});
Implement custom routing logic:
// Route based on content type
if (entry.data.contentType === 'product') {
return `/products/${entry.slug}`;
} else {
return `/${entry.slug}`;
}
Speed up builds with:
Enhance performance with:
Optimize delivery:
Set up content stages:
status: draft | review | published
Track content changes:
version: 2
previousVersions:
- slug: post-v1
date: 2025-01-15
Publish content at specific times:
publishDate: 2025-02-01T09:00:00Z
expiryDate: 2025-03-01T00:00:00Z
Expose content via API:
// src/pages/api/content.json.ts
export async function GET() {
const content = await getCMSContent();
return new Response(JSON.stringify(content));
}
Trigger rebuilds on content changes:
// Netlify webhook
POST https://api.netlify.com/build_hooks/YOUR_HOOK_ID
Enable draft previews:
if (Astro.url.searchParams.has('preview')) {
// Show draft content
}
Validate frontmatter:
schema: z.object({
title: z.string().max(100),
description: z.string().max(200),
// Prevent XSS
dangerousHtml: z.never(),
});
Restrict content access:
if (entry.data.private && !isAuthenticated) {
return Astro.redirect('/login');
}
For API-based sources:
wordpress: {
rateLimit: {
requests: 100,
period: 60000 // 1 minute
}
}
Track build performance:
Monitor content performance:
Implement error handling:
try {
const content = await getContent();
} catch (error) {
console.error('Content load failed:', error);
// Fallback behavior
}
Use hybrid mode during transition:
Build failures:
Routing conflicts:
Performance issues:
Learn how to integrate headless CMS platforms with your Astro site while maintaining the same content layer API and all existing features.