WordPress and Astro aren’t rivals—they serve different needs and can complement each other. If you’re considering a full migration from WordPress to Astro for performance, control, and simplicity (Markdown files, static builds, no PHP) or want to keep WordPress as a headless CMS while using Astro as your frontend, this guide walks you through decisions, commands, and production-ready patterns.
In one line: Astro gives you a static, component-based frontend with zero JS by default; WordPress gives you a familiar editorial UI and ecosystem. You can migrate entirely or decouple (headless) to get the best of both worlds.
1) When does Astro make sense, and when should you keep WordPress?
Pick Astro if:
- Your site is content-driven (blog, docs, portfolio) and you want top-tier Core Web Vitals and a smaller attack surface (no PHP or DB in the frontend).
- You’re comfortable with code workflows (Git, CI/CD) and Markdown/MDX.
- You want full control over components, styles, and deployment (Vercel/Netlify/Cloudflare/VPS/etc.).
Pick WordPress Headless + Astro if:
- You need a non-technical editorial experience, roles/reviews, and editorial plugins.
- You want to keep WordPress as your data source (REST/GraphQL) while serving that content via a fast, secure Astro frontend.
- You want strong SEO without giving up the WordPress dashboard.
2) Key similarities and differences
Similarities
- Both are great for content sites and support Markdown (WordPress needs a plugin).
- Both encourage building your UI from blocks/components (Gutenberg vs. Astro components).
Differences
- WordPress is edited in an online dashboard; Astro is edited in a code editor and built locally or in a cloud dev environment (IDX, StackBlitz, CodeSandbox, Gitpod).
- WordPress has a massive plugin/theme market; Astro offers starters and integrations, but you’ll often build more yourself (with more control and fewer moving parts).
- WordPress stores content in a database; Astro uses files (Markdown/MDX). In headless mode, Astro fetches content from WordPress via REST or GraphQL.
3) Option A — Full migration of a WordPress blog to Astro (content in Markdown)
Step 1: create a project with the blog starter
npm create astro@latest -- --template blog
# or: pnpm dlx create-astro@latest --template blog
# or: yarn create astro --template blog
Code language: CSS (css)
Step 2: export WordPress posts to Markdown
- Use a WP → Markdown exporter.
- Normalize frontmatter (title, date, slug, tags, categories) and fix image paths/links.
- For large/complex sites, export in batches and automate replacements (internal links, shortcodes).
Step 3: content structure in Astro
- Place posts under
src/content/orsrc/pages/blog/(depending on the starter). - If using Astro Content Collections, define a schema to validate metadata.
Step 4: components, styles, navigation
- Build/adapt layouts and components (header, footer, cards, pagination).
- Replace WP plugins (breadcrumbs, TOC) with Astro components or integrations.
Step 5: SEO, images, performance
- Use
@astrojs/sitemap,@astrojs/image, and proper meta tags. - Keep JS minimal: Astro ships 0 KB JS by default unless you add interactivity.
- Pre-render pages and serve through a CDN.
Step 6: deploy and domain
- Deploy to Vercel/Netlify/Cloudflare/Pages/VPS.
- Update DNS to keep the same URL.
- If keeping WP as archive/staging, remove it from the primary domain.
Pros: maximum performance/security/control; reduced backend ops.
Tradeoffs: no WP editor; editing via Markdown/Git (or adopt a headless CMS later).
4) Option B — WordPress Headless with Astro (keep WP as your CMS)
4.1. Expose data with WordPress API
- REST API is available by default:
https://YOUR-SITE/wp-json/wp/v2/ - Optional: WPGraphQL or Gato GraphQL if you prefer GraphQL.
- Unauthenticated REST reads public content only. For private content/actions, add auth (JWT/app passwords/etc.).
REST example (posts):
const res = await fetch("https://[YOUR-SITE]/wp-json/wp/v2/posts");
const posts = await res.json();
Code language: JavaScript (javascript)
Render in Astro:
<h1>Astro + WordPress 🚀</h1>
{posts.map((post) => (
<>
<h2 set:html={post.title.rendered} />
<p set:html={post.content.rendered} />
</>
))}
Code language: HTML, XML (xml)
Optimize the response
- Limit fields:
?_fields=author,id,excerpt,title,link - Include related resources:
?_embed(featured image, author, etc.)
4.2. Lists and dynamic routes in Astro
List page (src/pages/index.astro) using a “dinos” example:
import Layout from "../layouts/Layout.astro";
let res = await fetch("https://norian.studio/wp-json/wp/v2/dinos");
let posts = await res.json();
<Layout title="Dinos!">
<section>
<h1>List of Dinosaurs</h1>
{posts.map((post) => (
<article>
<h2>
<a href={`/dinos/${post.slug}/`} set:html={post.title.rendered} />
</h2>
<Fragment set:html={post.content.rendered} />
</article>
))}
</section>
</Layout>
Code language: JavaScript (javascript)
Dynamic page by slug (src/pages/dinos/[slug].astro):
import Layout from '../../layouts/Layout.astro';
const { slug } = Astro.params;
let res = await fetch(`https://norian.studio/wp-json/wp/v2/dinos?slug=${slug}`);
let [post] = await res.json();
// Required for SSG (not for SSR)
export async function getStaticPaths() {
let data = await fetch("https://norian.studio/wp-json/wp/v2/dinos");
let posts = await data.json();
return posts.map((post) => ({
params: { slug: post.slug },
props: { post },
}));
}
<Layout title={post.title.rendered}>
<article>
<h1 set:html={post.title.rendered} />
<Fragment set:html={post.content.rendered} />
</article>
</Layout>
Code language: PHP (php)
Featured image with _embed:
const { slug } = Astro.params;
let res = await fetch(`https://norian.studio/wp-json/wp/v2/dinos?slug=${slug}&_embed`);
let [post] = await res.json();
<img src={post._embedded['wp:featuredmedia']['0'].media_details.sizes.medium.source_url} />
Code language: JavaScript (javascript)
4.3. Editorial flow and deployment
- Editors keep using WordPress to write/publish.
- The Astro frontend is built/deployed separately (CI/CD). Options:
- SSR/ISR: dynamic serving + caching (fewer builds).
- SSG + Webhooks: publishing in WP triggers a rebuild of the Astro site.
- Keep the same domain pointing to Astro. WP can live on a subdomain (e.g.,
cms.yourdomain.com) or behind restricted access.
Pros: robust editorial UX plus fast frontend; decoupling lets each layer evolve independently.
5) SEO, performance, and security: best practices
- Sitemap + robots:
@astrojs/sitemap, properrobots.txt. - Meta & structured data: a reusable
Headcomponent (OpenGraph, Twitter, JSON-LD). - Images:
@astrojs/imagefor resize/AVIF/WebP; serve through a CDN. - Minimal JS: Astro ships zero JS by default; add interactivity with islands only where needed.
- Caching: cache static HTML and assets at your CDN; set TTLs and invalidation for SSR.
- Security: in headless setups, hide the public WP if not needed; rate-limit API; configure CORS.
6) Production & deployment
- Hosting: Vercel, Netlify, Cloudflare Pages, Render, fly.io, or your VPS (Nginx).
- CI/CD (GitHub Actions, GitLab CI, etc.). Typical pipeline:
npm cinpm run build- deploy via
vercel deploy/netlify deploy/ rsync to your server
- Domains & TLS: enforce HTTPS, set redirects, test dynamic routes.
7) Quick migration checklist
- Audit posts, pages, media, taxonomies, and URLs in WP.
- Decide A (migrate) or B (headless).
- Create the Astro starter & content structure.
- Rebuild navigation, layouts, and key components.
- Integrate the API (if headless) & define dynamic paths.
- Add SEO, sitemap, images, analytics.
- Configure CI/CD & domain.
- Test performance (Lighthouse, WebPageTest) & accessibility.
- Plan 301 redirects if URLs change.
8) FAQs
Can I keep the WordPress editor with Astro?
Yes. In headless mode, WP remains your CMS. Astro consumes REST/GraphQL and renders an ultra-fast frontend.
Do I need auth to read the WP API?
For public content, no (the REST API reads without auth). For private content or write actions, set up auth (JWT/app passwords/etc.).
What about SEO when moving to Astro?
Astro outputs fast static HTML, great for SEO. Preserve slugs and add redirects if URLs change. Include sitemap, meta, and structured data.
What happens to my WP plugins?
In a full migration, replace plugin features with Astro components/integrations. In headless, plugins that modify content (e.g., custom fields) still apply if their output is exposed via the API.
How do I handle featured images and media?
Use ?_embed in REST to retrieve featured media and sizes. Serve images through a CDN and consider @astrojs/image for optimization.
Resources & references
- Official guide: Migrating from WordPress — steps and blog starters.
- Headless WordPress & Astro — REST/GraphQL, dynamic routes,
_embed. - Starters on astro.new and the theme showcase.
- Astro Build a Blog tutorial — ideal if you’re new to Git/GitHub workflows.
Sources: Astro documentation (Migrating from WordPress, Headless WordPress & Astro) and public examples using the WordPress REST API with Astro.
