Build an Astro Content Site with UnfoldCMS in 30 Minutes

Astro 5 Content Layer API + UnfoldCMS API + free Cloudflare Pages deploy

June 8, 2026 · 11 min read
Build an Astro Content Site with UnfoldCMS in 30 Minutes

Astro just topped the State of JS 2025 Satisfaction ranking — #1 by 39 points over Next.js, with 55,200 GitHub stars and a clear lead in static-site generation as the fastest-growing meta-framework. Pair Astro with a real CMS and you get production marketing sites that ship 40% faster load times and 90% less JavaScript than the Next.js equivalent, with editors getting a polished admin UX instead of "edit this Markdown file in VS Code."

TL;DR: Pair Astro 5+ with UnfoldCMS and you get a fully static content site with a real admin — posts, pages, categories, SEO fields — in 30 minutes. Astro fetches at build time via the Content Layer API; UnfoldCMS handles all editing. Deploy to Cloudflare Pages for free.

This is the 30-minute path from git clone to a deployed Astro site backed by UnfoldCMS — the only CMS today with an admin built entirely on shadcn/ui. By the end you'll have an Astro 5 / 6 front-end consuming UnfoldCMS's public API at build time, type-safe content via the new Content Layer API, deployed to Cloudflare Pages on the free tier.

Why this stack works: Astro for the public site means SSG, edge caching, near-zero client JS for static pages, and the new Content Layer API for type-safe external CMS data. UnfoldCMS for the admin means a fully built shadcn/ui CMS — 51 components, 205 admin pages — without writing any admin UI yourself.

What you'll build

A two-service setup:

  • Front-end — Astro 5 / 6 site, statically generated, deployed on Cloudflare Pages (unlimited bandwidth on the free tier).
  • Backend — UnfoldCMS on localhost:8000 (or your own host) serving the admin (shadcn-based) plus a public JSON API at /api/blog/posts and /api/v1/*.

At build time, Astro fetches posts from UnfoldCMS, runs them through Content Layer schema validation, and emits static HTML. Editors update content in the CMS; you trigger a rebuild (manually or via webhook) and the static site updates.

Why Astro 5+ Content Layer API matters

If you've used Astro before Astro 5, the content story has changed.

  • Astro 5 shipped the Content Layer API as the replacement for the legacy type: "content" collections.
  • Astro 6 dropped all legacy support — Content Layer is the only path forward.
  • Loaders: glob() for local Markdown/MDX; file() for single-source structured data; custom loaders for external APIs like UnfoldCMS.
  • Performance: 5× faster Markdown processing, 2× faster MDX, 25–50% less memory compared to v4.

For external CMS data, the Content Layer + custom loader pattern is the current best practice — and it gives you the type safety + caching benefits that the legacy fetch-only approach didn't.

Source: Astro v5 upgrade guide, Content Layer API reference.

Prerequisites

You need PHP 8.3+, Composer, Node 20+, pnpm, and MySQL or SQLite for the CMS side. For the Astro side, just Node 20+ and pnpm. Cloudflare account (free) for deployment.

If you're on macOS with Homebrew: brew install php composer node pnpm mysql. On Linux, the equivalent apt/dnf packages.

This tutorial assumes basic terminal comfort. No prior Astro or Laravel experience required.

Step 1 — Spin up UnfoldCMS (10 minutes)

If you already followed Build a Blog CMS with Next.js + shadcn/ui in 30 Minutes, you have UnfoldCMS running. Skip to Step 2. Otherwise:

git clone https://github.com/hpakdaman/unfoldcms.git cms
cd cms
composer install
pnpm install
cp .env.example .env
php artisan key:generate

SQLite for local dev — edit .env:

DB_CONNECTION=sqlite
DB_DATABASE=/absolute/path/to/cms/database/database.sqlite

Then:

touch database/database.sqlite
php artisan migrate --seed
pnpm run build
php artisan serve

UnfoldCMS runs at http://localhost:8000. Log in at /admin with the seeded admin account (printed to your terminal). Create a test post — title, body, mark published. Save.

Step 2 — Spin up Astro 5 / 6 (5 minutes)

pnpm create astro@latest frontend

Choose: "Use blog template" or "Empty" — both work. Pick TypeScript (strict).

cd frontend
pnpm install
pnpm dev

Astro runs at http://localhost:4321.

Step 3 — Create a custom Content Layer loader for UnfoldCMS

This is the part that differs from a Next.js tutorial. Instead of fetch() calls scattered through page components, you write one loader that pulls UnfoldCMS posts at build time, then Astro caches and type-checks them via Content Collections.

Edit src/content.config.ts (note: this path moved from src/content/config.ts in Astro 5):

import { defineCollection, z } from "astro:content";

const posts = defineCollection({
  loader: async () => {
    const res = await fetch("http://localhost:8000/api/blog/posts?per_page=100");
    const json = await res.json();
    const list = json.data?.data ?? [];

    return list.map((post: any) => ({
      id: String(post.id),
      slug: post.slug,
      title: post.title,
      short_description: post.short_description,
      body: post.body,
      posted_at: post.posted_at,
    }));
  },
  schema: z.object({
    slug: z.string(),
    title: z.string(),
    short_description: z.string().nullable(),
    body: z.string(),
    posted_at: z.string(),
  }),
});

export const collections = { posts };

What this gets you:

  • Type-safe access to all UnfoldCMS posts via getCollection("posts").
  • Build-time fetching — no client-side API calls.
  • Zod schema validation — if the API ever returns malformed data, your build fails loudly instead of silently shipping a broken page.

Step 4 — Render the blog index

Replace src/pages/index.astro:

---
import { getCollection } from "astro:content";

const posts = await getCollection("posts");
posts.sort((a, b) =>
  new Date(b.data.posted_at).getTime() - new Date(a.data.posted_at).getTime()
);
---

<html lang="en">
  <head>
    <title>Blog</title>
    <meta name="viewport" content="width=device-width, initial-scale=1" />
  </head>
  <body>
    <main style="max-width: 720px; margin: 0 auto; padding: 2rem;">
      <h1>Blog</h1>
      <ul>
        {posts.map((post) => (
          <li>
            <a href={`/blog/${post.data.slug}/`}>{post.data.title}</a>
            <p>{post.data.short_description}</p>
            <time>{new Date(post.data.posted_at).toLocaleDateString()}</time>
          </li>
        ))}
      </ul>
    </main>
  </body>
</html>

Reload localhost:4321. Your UnfoldCMS test post should render as a link.

Step 5 — Render a single post page

Create src/pages/blog/[slug].astro:

---
import { getCollection } from "astro:content";

export async function getStaticPaths() {
  const posts = await getCollection("posts");
  return posts.map((post) => ({
    params: { slug: post.data.slug },
    props: { post },
  }));
}

const { post } = Astro.props;
---

<html lang="en">
  <head>
    <title>{post.data.title}</title>
  </head>
  <body>
    <article style="max-width: 720px; margin: 0 auto; padding: 2rem;">
      <h1>{post.data.title}</h1>
      <time>{new Date(post.data.posted_at).toLocaleDateString()}</time>
      <div set:html={post.data.body} />
    </article>
  </body>
</html>

Click a post on the index — the detail page renders, content piped from UnfoldCMS, statically generated by Astro.

Step 6 — Deploy to Cloudflare Pages (5 minutes)

Cloudflare Pages is the best free host for pure-static Astro: unlimited bandwidth on the free tier, 500 builds/month. Vercel and Netlify both cap bandwidth at 100 GB/month on hobby tiers and Netlify moved to credit-based builds in 2025.

Install the Cloudflare adapter (for static output, no adapter is strictly required, but it gives you the wrangler deploy path):

pnpm astro add cloudflare

Build locally:

pnpm run build

Deploy via Wrangler (Cloudflare's CLI):

pnpm dlx wrangler pages deploy dist

That deploys your static site to a *.pages.dev URL. Add a custom domain in the Cloudflare dashboard if you want.

For production, swap the hardcoded http://localhost:8000 in your loader to https://cms.yoursite.com — wherever you host UnfoldCMS. Set it as an env var (CMS_URL) and read via import.meta.env.CMS_URL for portability.

Source on hosting comparison: Cloudflare Pages vs Netlify vs Vercel 2026.

What you just shipped

In about 30 minutes:

  • Astro 5 / 6 static site with type-safe content via Content Layer API.
  • UnfoldCMS backend with full shadcn admin (51 components, 205 admin pages).
  • Build-time content fetching — no client-side API calls, near-zero JS, lightning-fast pageloads.
  • Cloudflare Pages deploy on the free tier with unlimited bandwidth.

This is roughly the same shape companies like IKEA, Porsche, Unilever, Microsoft, and The Guardian use for content-heavy marketing sites (per Astro's vendor showcase). The only difference is you're using UnfoldCMS instead of a paid headless CMS for the content layer.

For the broader CMS picker, see I Tested 7 CMS Options for shadcn/ui — Here's What Works and Best CMS for Indie SaaS Founders.

Rebuild strategy — how content updates ship

Static sites are static — content updates require a rebuild. Options:

  • Manual: editors hit a "Trigger rebuild" button in the CMS that calls Cloudflare's deploy webhook. Simple, works fine for low-cadence sites.
  • Cron: scheduled rebuilds every N hours. Good for sites with predictable publishing.
  • Webhook on publish: UnfoldCMS calls Cloudflare's deploy webhook when a post is published or updated. Real-time-ish (build takes 30s–2min).

The current Astro build benchmark is 35–127 pages/second with optimization — for a 200-page site, that's roughly 2–6 seconds of build time, plus Cloudflare's deploy time (~30s end-to-end).

Performance — why Astro + UnfoldCMS pairs so well

The numbers that matter:

  • Astro ships 90% less JS than the Next.js equivalent for content sites (markaicode.com benchmark).
  • 40% faster load times on equivalent content pages.
  • Astro Image component (<Image /> / <Picture />) handles responsive images + lazy-loading automatically.
  • UnfoldCMS Spatie Media Library generates image conversions server-side — you fetch the URL, Astro picks up the optimised version.

For a static marketing site or blog, this is as fast as the web gets. Lighthouse 100s on every Core Web Vital are realistic, not aspirational.

People Also Ask

What is the best CMS for Astro?

Depends on use case. For a CMS where the admin is also shadcn/ui (same design system as your Astro site), UnfoldCMS is the only option. For purely headless with bring-your-own-admin: Contentful, Sanity, Hygraph, Payload, Strapi. For flat-file content with no CMS UI: Astro Content Collections + Markdown files.

Can you use Astro without a CMS?

Yes — Markdown / MDX files in src/content/ via Content Collections work fine for sites with 1–200 entries updated by developers. CMS recommended when you have non-dev editors, content volumes past ~200 entries, or a draft / publish / schedule workflow.

How do you fetch data from a headless CMS in Astro?

Two paths: (1) Custom Content Layer loader in src/content.config.ts (recommended in Astro 5+) for type-safe build-time fetching with Zod validation. (2) Plain fetch() in Astro components for simple cases. The loader approach is cleaner because it integrates with Content Collections.

Does Astro work with WordPress as a CMS?

Yes, via the WordPress REST API. Same pattern as UnfoldCMS — custom Content Layer loader pointing at /wp-json/wp/v2/posts. Astro has an official WordPress integration guide.

What's the fastest hosting for an Astro site?

For pure static Astro: Cloudflare Pages wins on free-tier bandwidth (unlimited vs Netlify/Vercel's 100GB/month). Vercel wins on Next.js-specific features that Astro doesn't need. Netlify is fine but moved to credit-based builds. Pick Cloudflare unless you have a specific reason not to.

Bottom line

Astro 5 / 6 + UnfoldCMS is the fastest path I know to a real, production-grade content site: editor-friendly admin, type-safe content fetching, build-time SSG, free hosting on Cloudflare Pages. The 30-minute build above shipped a complete pipeline; the rest of your project is just adding pages.

Want to skip the local setup and see UnfoldCMS live? Try the demo or see pricing.


Sources and methodology

  • Astro Content Layer APIAstro Content Collections docs, Content Layer Deep Dive, v5 upgrade guide, v6 upgrade guide.
  • Astro performance benchmarks — 90% less JS / 40% faster load times from markaicode.com; 5x faster Markdown processing from Astro's official Content Layer deep dive.
  • Astro market share + satisfaction — State of JS 2025 (#1 satisfaction, +39 over Next.js), W3Techs (9.08% SSG market share, #3 behind Next.js 58.55% and Nuxt 23.38%), GitHub (55,200 stars as of June 2026 via technologychecker.io).
  • Astro Image componentofficial images guide.
  • Hosting comparisonCloudflare Pages vs Netlify vs Vercel 2026, Astro hosting comparison.
  • UnfoldCMS countsfind cms/resources/js/components/ui -name "*.tsx" \| wc -l = 51; find cms/resources/js/pages/admin -name "*.tsx" \| wc -l = 205.
  • UnfoldCMS public API/api/blog/posts and /api/v1/* confirmed in cms/routes/web.php and cms/routes/api.php.
  • Tutorial steps tested end-to-end on macOS and Ubuntu 22.04, June 2026, against Astro 5 stable.

Free & Open Source

Own your CMS. No subscriptions.

Unfold CMS is free to download and self-host. Built on Laravel + React, full source code included.

Share this post:

Discussion

Comments (0)

Leave a Comment

Please log in to leave a comment.

Don't have an account? Register here

No comments yet. Be the first to share your thoughts!

Keep Reading

Related Posts

Back to all posts
Powered by UnfoldCMS