Shipping Zero JavaScript Websites with Astro and Contentful
by Ayham Kteash, Software Engineer
In the age of JavaScript-heavy frameworks, it's refreshing to build websites that ship zero JavaScript to the browser while still enjoying a modern developer experience. Astro makes this possible, and when paired with Contentful as a headless CMS, you get the best of both worlds.
Why Zero JavaScript?
Shipping less JavaScript means:
- Faster load times - No framework overhead
- Better Core Web Vitals - Improved LCP, FID, and CLS scores
- Improved accessibility - Works without JS enabled
- Lower bandwidth - Great for users on slow connections
Setting Up Your Astro Project
First, create a new Astro project:
npm create astro@latest my-blog
cd my-blog
npm install contentful
Fetching Content from Contentful
Create a simple content fetcher in src/lib/contentful.ts:
import contentful from 'contentful';
const client = contentful.createClient({
space: import.meta.env.CONTENTFUL_SPACE_ID,
accessToken: import.meta.env.CONTENTFUL_ACCESS_TOKEN,
});
export async function getBlogPosts() {
const entries = await client.getEntries({
content_type: 'blogPost',
order: ['-sys.createdAt'],
});
return entries.items.map((item) => ({
slug: item.fields.slug,
title: item.fields.title,
body: item.fields.body,
publishedAt: item.sys.createdAt,
}));
}
Building Static Pages
Now create a page that fetches content at build time. In src/pages/blog/[slug].astro:
---
import { getBlogPosts } from '../../lib/contentful';
import Layout from '../../layouts/Layout.astro';
export async function getStaticPaths() {
const posts = await getBlogPosts();
return posts.map((post) => ({
params: { slug: post.slug },
props: { post },
}));
}
const { post } = Astro.props;
---
<Layout title={post.title}>
<article>
<h1>{post.title}</h1>
<time>{new Date(post.publishedAt).toLocaleDateString()}</time>
<div set:html={post.body} />
</article>
</Layout>
The Magic: Zero JS Output
When you run npm run build, Astro generates pure HTML files. Check the output:
dist/
├── blog/
│ ├── my-first-post/
│ │ └── index.html
│ └── another-post/
│ └── index.html
└── index.html
No JavaScript bundles. Just HTML and CSS. Your users get instant page loads.
Adding Interactivity (When Needed)
Need a bit of interactivity? Astro's islands architecture lets you hydrate only what's necessary:
---
import Counter from '../components/Counter.jsx';
---
<!-- This component ships zero JS -->
<p>Static content here</p>
<!-- Only this component gets hydrated -->
<Counter client:visible />
Conclusion
Astro with Contentful gives you:
- A great authoring experience with a headless CMS
- Modern DX with TypeScript and components
- Zero JavaScript shipped to users by default
- Static site generation for maximum performance
Your website visitors will thank you for the fast experience.