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:

  1. A great authoring experience with a headless CMS
  2. Modern DX with TypeScript and components
  3. Zero JavaScript shipped to users by default
  4. Static site generation for maximum performance

Your website visitors will thank you for the fast experience.

Tell me about your project

My location

  • Berlin
    Germany