Skip to content

I migrated my blog from Next.js to Astro

Next.js was overkill, and Strapi meant constant clicking around the UI. I moved the blog to Astro and Cloudflare, posts live in the repo as MDX, infrastructure in OpenTofu, and Lighthouse shows 100/100.

Franek WojtasBuild in Public4 min read

Hey there!

Long time no see. Did you miss me?

So yeah, just like the title says. My blog originally ran on Next.js hosted on Vercel, with Strapi CMS hosted on Railway. But after a while I figured it wasn’t the best setup. That’s why I decided to migrate the blog to Astro without a dedicated CMS. If you want to read about why I picked Next.js and Strapi in the first place, check out my previous post on how I created a blog in Next.js and Strapi.

Why did I move away from Next.js and Strapi?

I have nothing against Next.js. I still think it’s a great framework. But in my case Next.js was a bit of an overkill. My blog doesn’t need most of the features Next.js offers. That’s why I decided to migrate the blog to Astro, which is lighter and a better fit for this kind of project. Next.js ships quite a lot of JS to the browser, and I don’t need that. I want my blog to be fast and light, so Astro is the better choice.

Then there’s the CMS. This project was my first time using Strapi. Overall a pretty decent CMS, but not for me. I’m not a big fan of clicking around the UI. Every change, every new post meant logging into Strapi and clicking around. It’s way more convenient to have everything in one place, which means the repo. I was writing posts in Markdown in Strapi anyway, so why would I need a CMS? Locally I can just fire up the dev server and test everything right away, while in Strapi setting up live preview required extra configuration. And then there’s maintaining two separate projects. More work with updates, two separate apps to look after, extra hosting costs. And now? Everything in one place, one project, one hosting, static posts in Markdown that I can edit in VS Code. Simple and convenient.

Before: Next.js + Strapi

Before: Next.js + StrapiNext.js on Vercel fetches content through the API of Strapi hosted on Railway and images from Cloudflare R2. The domain is on Cloudflare.VercelNext.jsSSG + ISRRailwayStrapi CMSadmin panelCloudflareR2storageDNSdomaincontentimages

After: Astro

After: AstroThe whole blog runs on Cloudflare: Astro with content and images in the repository, the domain, and R2 holding the OpenTofu state.CloudflareAstrocontent + images in repoDNSdomainR2OpenTofu state
Before: Vercel, Railway and Cloudflare. After: everything on Cloudflare, content in the repo.

Hosting on Cloudflare instead of Vercel

In the new version I moved the blog to Cloudflare. The main reason was the hosting cost, and since I already had the domain and R2 on Cloudflare anyway, that was a nice bonus. I wrote a simple CI/CD that deploys a new version of the blog with Cloudflare Wrangler after a PR gets merged. Nothing fancy, but very convenient (Vercel had that too). For a moment I also wanted to add a staging environment on a subdomain, but luckily I quickly realized that for a blog I build on my own it’s definitely overkill. I had to click a few things in the UI, and as I mentioned before, I’m not the biggest fan of that. So in a flash of inspiration I decided to add infrastructure as code, which fits my case perfectly. And the choice fell on OpenTofu.

Request flow on buildandmarket.devA browser request reaches Cloudflare (TLS, WAF, www redirect). Most paths are served as static assets without running the Worker. The Worker handles only three paths: / picks the language, /_actions/* runs Astro Actions, and /a/* proxies PostHog.BrowserCloudflareTLS, WAF, www redirectStatic assetsHTML, CSS, 404.htmlWorker
Most requests are served by static assets. The Worker only runs for three endpoints.

Infrastructure as code with OpenTofu

OpenTofu, my pick for IaC. For the uninitiated, it’s a fork of Terraform that was created in response to Terraform’s license change. This tool lets you configure environments and settings on platforms like Cloudflare or AWS. Everything you can click through in the UI can be configured in code. A dream come true. For the blog the configuration is pretty simple, no fancy bells and whistles, but it’s super convenient. Whenever something changes, you just add a tiny bit of code, run a few commands and you’re done. Zero clicking around the UI.

resource "cloudflare_workers_custom_domain" "production" {
  account_id = var.account_id
  zone_id    = var.zone_id
  hostname   = "buildandmarket.dev"
  service    = "buildandmarket"
}

Performance after the migration: 100/100 in Lighthouse

Astro is known for shipping minimal JS to the browser, so it’s no surprise that the blog flies. Without much effort, Lighthouse showed 100/100 across the board. On top of that there are static files and Cloudflare caching, so the blog has no choice but to fly.

Google PageSpeed Insights results for the blog after migrating from Next.js to Astro. Performance 100/100, accessibility 100/100, best practices 100/100, SEO 100/100.

Now all that’s left is the most important part of the blog: writing posts.