Migrating a Static Site from Eleventy to Astro

Migrating a Static Site from Eleventy to Astro

We rebuilt this website on Astro. Eleventy served us well, and nothing was broken, but our other client projects already run on Astro, and one toolset for every static site means less context switching and shared components.

A framework migration is only a success if visitors and search engines don't notice it. Here is the process we followed.

1. Snapshot the Old Build First

Before touching any code, we built the existing site into a separate folder. That snapshot became the reference for everything that followed: every page, the feed, the sitemap, and the 404 page.

npx eleventy --output=../site-before

2. Keep Every URL

Broken URLs cost rankings and backlinks. Astro's file-based routing made it straightforward to match the old structure exactly:

src/pages/blog/[year]/[month]/[day]/[slug].astro  -> /blog/2026/11/12/<slug>/
src/pages/services/[slug].astro                    -> /services/<slug>/
src/pages/feed.xml.ts                              -> /feed.xml
src/pages/404.astro                                -> /404.html

Setting build.format: 'directory' keeps the trailing-slash URLs we already had.

3. Treat Content as Data

Instead of re-rendering Markdown with a different parser (and risking small changes in output), we stored each post as HTML with a YAML header and rendered it as-is. The page shell, SEO tags, and structured data live in one Astro layout.

const postFiles = import.meta.glob('../content/posts/*.html', {
  query: '?raw', import: 'default', eager: true,
});

4. Port Templates, Not Just Pages

Nunjucks includes became Astro components: header, footer, breadcrumbs, and icons. Filters such as date formatting and excerpt truncation became small TypeScript functions. One gotcha: Astro treats curly braces in markup as expressions, so raw code samples with { and } go through set:html.

5. Diff the Builds

With both builds on disk, a short script compared every page: visible text, links, images, meta tags, and the parsed JSON-LD. The diff surfaced one old template bug (service pages showed the wrong name in their breadcrumb), which we fixed on purpose. Everything else matched.

6. Switch Hosting Carefully

We moved deployment to Cloudflare Pages connected to the Git repository, so every push builds automatically and every branch gets a preview URL. Two lessons from that step:

  • Ship a real 404.html, or the host may answer unknown URLs with your home page and a 200 status
  • Move custom domains only after the new deployment is verified, and do it at a quiet time

Was It Worth It?

For visitors nothing changed, which was the goal. For us, the site now shares tooling with our other projects, components are typed, and adding a page is a single file.

Thinking about moving your site to a new stack without losing traffic? Let's talk.