How to Use Contentful with Astro: Complete Guide
Step-by-step guide to integrating Contentful with your Astro website. Installation, configuration, and best practices.
Contentful is one of the most popular enterprise headless CMS platforms out there, and it pairs really well with Astro. You get a powerful content modeling API on the backend and lightning-fast static pages on the frontend. If you want structured content without the weight of a traditional CMS, this is a solid combo.
Prerequisites
- Node.js 18+
- An Astro project (
npm create astro@latest) - A Contentful account (free tier gives you 5 users and 25,000 records)
Installation
Install the Contentful JavaScript SDK:
npm install contentful
Configuration
Create a .env file in your project root with your Contentful credentials. You can find these in your Contentful dashboard under Settings > API Keys.
CONTENTFUL_SPACE_ID=your_space_id
CONTENTFUL_DELIVERY_TOKEN=your_delivery_api_token
CONTENTFUL_PREVIEW_TOKEN=your_preview_api_token
Now create a helper file to initialize the Contentful client:
// src/lib/contentful.ts
import contentful from "contentful";
export const contentfulClient = contentful.createClient({
space: import.meta.env.CONTENTFUL_SPACE_ID,
accessToken: import.meta.env.DEV
? import.meta.env.CONTENTFUL_PREVIEW_TOKEN
: import.meta.env.CONTENTFUL_DELIVERY_TOKEN,
host: import.meta.env.DEV ? "preview.contentful.com" : "cdn.contentful.com",
});
This setup automatically switches between the preview API during development and the delivery API in production.
Basic Usage
Let's say you have a "Blog Post" content type in Contentful with fields like title, slug, body, and publishDate. Here's how to fetch and render posts:
---
// src/pages/blog/index.astro
import { contentfulClient } from "../../lib/contentful";
import BaseLayout from "../../layouts/BaseLayout.astro";
const entries = await contentfulClient.getEntries({
content_type: "blogPost",
order: ["-fields.publishDate"],
});
const posts = entries.items;
---
<BaseLayout title="Blog">
<h1>Blog</h1>
<ul>
{posts.map((post) => (
<li>
<a href={`/blog/${post.fields.slug}`}>
<h2>{post.fields.title}</h2>
<time>{new Date(post.fields.publishDate).toLocaleDateString()}</time>
</a>
</li>
))}
</ul>
</BaseLayout>
Fetching Data for Dynamic Pages
For individual blog post pages, use getStaticPaths to generate routes at build time:
---
// src/pages/blog/[slug].astro
import { contentfulClient } from "../../lib/contentful";
import { documentToHtmlString } from "@contentful/rich-text-html-renderer";
import BaseLayout from "../../layouts/BaseLayout.astro";
export async function getStaticPaths() {
const entries = await contentfulClient.getEntries({
content_type: "blogPost",
});
return entries.items.map((item) => ({
params: { slug: item.fields.slug },
props: { post: item },
}));
}
const { post } = Astro.props;
const htmlContent = documentToHtmlString(post.fields.body);
---
<BaseLayout title={post.fields.title}>
<article>
<h1>{post.fields.title}</h1>
<div set:html={htmlContent} />
</article>
</BaseLayout>
You will need the rich text renderer for this:
npm install @contentful/rich-text-html-renderer
Production Tips
Cache aggressively. Contentful's delivery API is fast, but fetching on every build still adds up. Use Astro's static output mode and rebuild only when content changes via webhooks.
Use the include parameter. When fetching entries with linked content (images, references), set include: 2 to resolve nested entries in a single API call instead of multiple round trips.
Set up webhooks for rebuilds. In Contentful, go to Settings > Webhooks and trigger a rebuild on your hosting platform (Vercel, Netlify, etc.) whenever content is published or unpublished.
Type your content models. Use contentful-typescript-codegen to auto-generate TypeScript interfaces from your Contentful content models. Saves you from guessing field names.
Optimize images. Contentful's Images API supports on-the-fly resizing and format conversion. Append ?w=800&fm=webp to image URLs for smaller payloads.
Alternatives to Consider
- Sanity if you want real-time collaboration and a more flexible schema builder. It also has an official Astro integration.
- Keystatic if you prefer a git-based CMS that stores content directly in your repo. Zero external dependencies.
- Storyblok if you need a visual editor for non-technical content teams. Great drag-and-drop experience.
Wrapping Up
Contentful and Astro make a strong pair for content-heavy sites that need structure and performance. Set up the client, model your content, fetch it at build time, and you have a fast, maintainable site with a proper CMS behind it.
Related Articles
How to Use Algolia with Astro: Complete Guide
Step-by-step guide to integrating Algolia with your Astro website.
How to Integrate Auth0 with Astro: Complete Guide
Step-by-step guide to integrating Auth0 with your Astro website. Setup, configuration, and best practices.
How to Use AWS Amplify with Astro: Complete Guide
Step-by-step guide to integrating AWS Amplify with your Astro website.