Skip to main content
Web Design & Development

What WordPress Developers Should Know Before Building With Astro

This is for WordPress developers, designers, agency owners and technical content teams who are comfortable working near a codebase, or working with someone who is. You do not need to be a backend engineer to build a useful Astro site. You do need to be comfortable making decisions about content, components, deployment and publishing that WordPress normally makes for you.

It is not a setup tutorial. If you need to install Astro and build a first page, Astro’s own tutorial is the right starting point. This begins after that page works, at the decisions that turned out to be expensive to reverse.

If you only do four things:

  1. Choose the content source and name who can publish before you build templates.
  2. Put publishing eligibility in one shared utility.
  3. Add schema, link and metadata checks for production invariants.
  4. Make every blocking check fail on purpose, once.

Should You Build This Site With Astro

Answer this before anything else, because the rest of the article assumes yes.

Astro fits marketing sites, documentation, editorial and advocacy sites, and content-led projects generally. The common thread is content that changes on a publishing schedule rather than by the second.

A static site can also be cheap to host without being cheap to operate, once it needs non-technical publishing, forms, preview environments, dynamic data or integrations. Plan differently if non-technical editors need a visual editing surface, if your content is highly relational, or if pages must reflect data that changes between builds. Astro can participate in all three once you add the necessary CMS, preview or server-side architecture. That is a different operating model from the file-based workflow this article focuses on.

Three projects, sorted:

A five-page local services site with occasional case studies is a straightforward fit. A policy site with hundreds of guides, scheduled publishing and custom data visualizations also fits, provided the editorial workflow is designed early. A member portal with account-specific data and a support team changing prices through the day needs on-demand rendering at minimum, and probably a different plan.

Four terms, used throughout:

  • Static generation: Astro writes HTML during a build, not when a visitor arrives.
  • Content collection: a named, schema-validated set of entries such as posts or guides.
  • Frontmatter: metadata at the top of a Markdown or MDX file, like title and publish date.
  • MDX: Markdown that can render components in the document body.

What a Front-End Builder Needs

You do not have to become a backend developer first. For a marketing site, a blog, documentation or a content hub, HTML, CSS, some JavaScript, Markdown, Git and deployment basics are enough to start. The real shift is from editing pages in an admin screen to working with a project’s source files.

Most of what you already know transfers. It changes name and gains a rule.

In WordPressIn AstroWhat to learn
Theme templateLayout or page templateHow .astro files compose a page
Header, footer, share widgetComponentBuild once, update everywhere
Custom fieldsFrontmatter and a schemaDefine required metadata before content grows
Categories and tagsSchema fields and collectionsDesign the taxonomy deliberately
Gutenberg blockAn approved MDX componentKeep the set small and documented
The Publish buttonA push, then a deployKnow exactly what triggers production
RevisionsGit historyEnough Git to review and restore
PluginA component, integration or serviceDecide whether the feature earns its keep
Page-builder stylingCSS and reusable componentsFix a pattern once, not page by page

The row that costs the most if you skip it is custom fields. WordPress lets a content model grow by accident, one field at a time. A schema makes you state it up front, and a missing description becomes a build error naming the file rather than a blank tag in production.

Astro Separates What WordPress Usually Combines

In a conventional WordPress setup, content management, theme rendering and publishing are tightly connected in one application, normally on one hosting stack. Without full-page caching, pages are assembled through PHP and database queries at request time.

Astro lets you pick each layer independently, and the stages run in this order:

  1. Content source

    Files in the repo, a headless CMS, an API, or WordPress.

  2. Schema validation

    Frontmatter checked against rules before anything renders.

  3. Build checks

    Links, metadata and prose, run against the built output.

  4. Deployment

    Blocked when a required check fails.

  5. Hosting

    A CDN, plus a server for any route that needs one.

We run three content sources on one rendering layer. A JSON config, MDX collections with schema validation, and a WordPress install queried through its REST API. The useful part is not that three are possible. It is that the content source can change without replacing the public rendering layer.

Two qualifications worth making, because the usual version of this pitch overstates both.

WordPress can run headless and fill that third slot perfectly well. Astro makes a decoupled front end straightforward without requiring one. You still choose the content source, the deployment workflow and any CMS integration.

And the security story is narrower than “no database to hack.” A fully static front end can take WordPress, PHP and the database out of the public page-request path, reducing exposure to one common class of risk. It does not secure or remove the WordPress origin, the CMS accounts, the repository, CI credentials, APIs or third-party services. Those are all still in scope, and that is where the interesting attacks now are.

Choose the Content Source Before You Build Pages

Changing this later can mean a content migration and retraining whoever publishes. It is one of the most expensive decisions to redo.

If you needStart withWhy
A small site maintained by developersMarkdown or MDX in the repoVersion control, portability, no external dependency
Editorial content with approved componentsMDX in the repoCallouts, diagrams, tables and other reusable editorial elements inside the body
Non-technical editors who need a dashboardHeadless WordPress or another headless CMSFamiliar editing surface, front end decoupled from the theme
Structured content shared across productsA headless CMS or your own APIOne source, many consumers

The question underneath this table is who edits. A small agency site where one developer publishes twice a month is often simplest to maintain as MDX in Git. A nonprofit with five communications staff who need drafts, revisions, media management and scheduled publishing is better served by WordPress as a headless CMS. Moving the front end to Astro preserves the editorial interface they already know, though it still adds integration, preview and deployment work that a coupled setup did not have.

Use Markdown, MDX or a CMS for Different Reasons

Use .md when posts are text, links, images and ordinary formatting. Use .mdx when writers need approved components inside the post body. Use a headless CMS when non-technical editors need a familiar publishing interface.

We began with more than ninety Markdown entries, then wanted data visualizations and styled callouts inside specific posts. Plain Markdown does not provide that component model, so converting later meant changing content format and component architecture at the same time. We ended up injecting visuals from the page template through a separate data file keyed by slug. It works. We would not choose it again.

If MDX is likely to be part of your publishing model, set its component rules early. Keep the approved set small, and pass components in from the rendering layer:

---
import { getEntry, render } from 'astro:content';
import Callout from '../../components/Callout.astro';

const entry = await getEntry('blog', 'post-1');
const { Content } = await render(entry);
---
<Content components={{ Callout }} />

This centralizes the import path, so content files keep using a stable <Callout /> name even when the implementation moves. It centralizes component-file imports; it does not make every change to the public MDX component API free. Keep the approved set small, and treat interactive components as a separate case: client-side hydration has different constraints from a static editorial component, and the two fail differently.

Define “Published” in One Place

A post is scheduled for Monday morning. The archive hides it correctly. The related-posts component does not. That URL is now public before its publish time, and you find out from Search Console.

That is what happened to us. Some pages called getCollection directly instead of going through a shared function, and future-dated content reached production.

WordPress hands every developer the same WP_Query conventions. Astro gives you getCollection('blog') and no opinion, so each page invents its own rules.

// src/lib/posts.ts
import { getCollection } from 'astro:content';

export async function getPublishedPosts(
  now = new Date()
) {
  const posts = await getCollection('blog');

  const live = posts.filter(
    ({ data }) => !data.draft && data.publishDate <= now
  );

  return live.sort((a, b) => {
    const dateDifference =
      b.data.publishDate.valueOf() -
      a.data.publishDate.valueOf();

    return dateDifference || a.id.localeCompare(b.id);
  });
}

The rule that transfers: pages decide where content appears, shared utilities decide whether it is eligible to appear at all. For any collection shown in more than one place, centralize that definition.

Two edge cases cost us real time. Sorting on date alone is not deterministic when entries share a timestamp, and ours did, which silently reordered previous and next links across fifteen posts. Add a stable tie-break. And a date-only value such as 2026-08-09 is a calendar date, not a publication timestamp. When JavaScript parses it as midnight UTC, it can become the previous evening in a US time zone. Ours published five hours early. Store a full timestamp with an offset, or define a publication time zone and convert deliberately.

Write this function before you build a second listing page.

A publish date answers whether an entry is eligible when the build runs. It does not start a build. If posts have to go live at a set time, add a scheduled build or a publish webhook from the CMS, then test the date filter and the deployment trigger separately, because either one can fail while the other looks fine.

Treat the Build as a Publishing Gate

A WordPress workflow can run checks before publication, but they are not inherently tied to a deployment. In an Astro workflow the build already has to succeed, which makes it a natural place to enforce production rules.

Be clear about what supplies what. Astro provides the build and rendering model. Everything else here is GitHub Actions, Cloudflare Pages and roughly a dozen scripts we wrote. WordPress teams can build the same thing, and some do. What Astro contributes is a build that already has to succeed, which is a natural place to hang a gate.

Our data

81,530
internal links verified on every build
Build pipeline on our largest site, check-internal-links.mjs Verified

For links inside its coverage, a broken one blocks deployment.

We also run prose checks. Readability is banded to a target grade, banned phrases and house vocabulary are enforced, and passive voice has a threshold. These enforce a narrow, testable slice of an editorial standard. They do not replace editing for clarity or judgment, and it would be a mistake to treat a passing score as a proxy for good writing.

The operating rule is simple. If a check protects a production invariant, it must block deployment when it fails. An advisory check can report without blocking, but only if it has an owner and somewhere the report gets read.

Prove That Your Quality Gate Can Fail

This is the lesson we paid the most for, and one we had not seen emphasized in Astro guidance.

Checks fail in two ways. They report a problem that is not real, which is noisy but visible. Or they report success while examining nothing, which is silent and survives for months.

Three of ours did the second thing.

What we checkedWhy it falsely passedWhat we changed
Prose qualityThe glob matched .md; the blog had become .mdx. Zero files scanned.Assert the file count is non-zero, then break a rule on purpose
Migration parityBoth sides of the comparison built from the same source, so it compared output to itselfConfirm the build actually read the source under test before trusting a pass
robots metadataThe layout accepted a robots prop and never read itAssert against rendered HTML, not component inputs

The prose one has a detail worth repeating, because the obvious fix was also wrong. We changed the glob to *.{md,mdx}. That implementation supports only * and **, so it treated the braces literally, matched nothing, and failed exactly as silently as the bug it replaced. We only caught it because we then checked the file count.

The robots case is the one that cost us most. A draft page passed robots="noindex, nofollow", looked correct in every review, and sat indexable on the production site for months with “DRAFT” in its title. An ignored property is worse than no property, because the declaration makes it look handled.

The pattern across all three is that each check was structurally incapable of failing, and nothing in its output said so.

A check you have never seen fail is not a verified check.

So we now break things on purpose. Feed the link checker a dead link. Point the schema at a malformed file. Sabotage each rule in turn and watch the build go red. It costs a few minutes per check, and it is the highest-return habit in this article.

Do Not Abstract Before the Second Use Case

The WordPress instinct is to install ahead of the need: Advanced Custom Fields because you might want custom fields, a page builder because layouts might get complicated.

The Astro version is building architecture before a second use case exists. We built a component routing system that read content types from JSON and dispatched to seventeen block components. It was genuinely elegant. It was also unnecessary, and we spent more time maintaining it than duplicating a few page templates would have cost.

Build the page in front of you. Extract the abstraction when a second real use case proves the pattern is shared.

What Publishing Looks Like Day to Day

Articles on this site are MDX files in a Git repository. A push triggers a build and a deploy, and our larger sites take longer because their builds run more checks before production.

Our data

155 pages, ~10 seconds
full production build of this site
Garrett Digital build, measured Verified

That is fast and dependable for someone comfortable with source-controlled content. It is not the same thing as an editorial workflow. There is no browser-based drafting, no visual preview for a non-technical writer, no scheduling UI, no media library, and no way for someone to publish without touching Git. A team that needs those should keep WordPress, or add a CMS and a preview environment on purpose rather than discovering the gap after launch.

Astro’s job here is not to replace the WordPress editor. It is the rendering and deployment layer, and it will take content from a repository, from WordPress, or from another CMS.

Headless WordPress Is Two Systems

We ran WordPress as the content source behind an Astro front end for months. It kept an editing interface people knew, and it added a second system to operate: API fetching, reshaping the content, character encoding, media handling, rebuild triggers, preview behavior and deployment timing. We solved most of it with custom integration work, and for a site maintained by the same small technical team that owns the front end, starting with MDX would have been simpler.

That is a tradeoff rather than a verdict. Headless WordPress is right when the editorial interface is valuable enough to justify the integration it adds. It often adds more integration than value when the same small technical team owns both the content and the front-end code.

Forms Are Where WordPress Feels More Complete

A contact form is a plugin decision in WordPress and an architecture decision in Astro. You choose the delivery service, the spam protection, the notification path, whether anything is stored, and where the credentials live. It is manageable, but it has costs and limits even when the first version fits inside a free or trial tier.

Decide early whether a submission should send an email, create a CRM record, store data, trigger an automation, or several of those. Keep API keys in environment variables rather than in the repository.

The Handoff Test

Ask who needs to change the site at four in the afternoon.

If the owner or the marketing team expects to update pricing, copy, an announcement or a page themselves, through a browser, a file-based Astro workflow is the wrong default. Keep WordPress, use another CMS, or budget for the editor, preview, permissions and deployment workflow that Astro does not bring with it.

If the client already sends those changes to a developer, the gap is smaller than it looks. Many of our WordPress clients already ask us to make production changes, and for them MDX in Git is fast and dependable. What changes is that they are explicitly hiring someone to own the technical layer.

The handoff itself is the part people underestimate. An Astro build is portable as source code, and that is not the same portability as a WordPress handoff. Whoever inherits it needs to be comfortable with Git, the deployment platform, the site’s components, and whatever sits behind the forms, email, search and dynamic routes.

Where AI Assistance Helps, and Where It Does Not

AI-assisted development made Astro easier for us to adopt than it would have been otherwise. It is good at building a component, tracing a deployment failure, writing a redirect rule, explaining an unfamiliar config.

It does not do the architecture. The content rules, the component boundaries, what renders statically and what renders on demand, which checks block a deploy: those are still decisions someone has to make and own. Treat AI output as a proposed implementation rather than evidence that an integration is secure, deployable, or consistent with how the rest of the project works. That is the same reasoning as making every quality gate fail on purpose.

It also has a recurring cost and a pricing model that moves, so treat it as development tooling you account for, the way you would design software or a managed code host, rather than something a single subscription covers forever.

The durable asset is not a particular assistant. It is the repository, and specifically the instructions in it. A maintained CLAUDE.md or AGENTS.md lets the next tool, or the next developer, understand the project’s rules without reconstructing them by reading everything.

Know What Static Publishing Does Not Solve

Astro’s default output: 'static' prerenders pages during the build. To render one route on demand you add a server adapter and opt that route out with export const prerender = false. If most routes need request-time rendering, use output: 'server' and opt individual routes back in with prerender = true. The separate “hybrid” mode name is gone in Astro 5. Verified against 5.18, and worth re-reading the rendering docs on a major version bump, because this is framework behavior rather than a durable principle.

The larger constraint is publishing latency. If a client changes an annual pricing page at 9 a.m. and a one-minute build is acceptable, static is a good fit. If an inventory count must be correct the moment a visitor opens the page, build time is the wrong source of truth and that route needs on-demand rendering.

Decide which of those you are before you commit to file-based content.

Price the Workflow, Not the Hosting

A small Astro site can be inexpensive to run. Static hosting, a repository, deployment and a simple contact form often start on free tiers, and for a five-page site that can hold for a long time.

That is not the same as a free platform. The cost moves rather than disappearing, into the services around the site.

LayerWhere it startsWhat creates cost later
Static hostingA free tier covers a small site comfortablyBuild frequency, on-demand functions, higher limits
Repository and CIA free plan is enough for one or two peoplePrivate-repo automation minutes, team seats
Content editingFiles in the repoEditor training, a CMS, a preview environment
Forms and email deliveryA free or trial tier on a transactional providerVolume, deliverability features, CRM automation
Images and mediaThe repository or a CDNLarge libraries, transformations, video
SearchStatic index or a hosted serviceIndex size, relevance tuning, query volume
AI-assisted developmentA developer subscriptionUsage, seats, changing plans
MaintenanceYour own timeDependency updates, integrations, failed deploys, support

Free tiers are a fine place to start and a poor thing to promise. Before telling a client a site costs nothing to run, document each service, who owns the account, which billing address receives the alert, the limit most likely to trigger an upgrade, and the fallback plan. Check the provider’s current pricing page before choosing a form, email, search or hosting service.

Your Astro Project Baseline

  1. Choose the content source and authoring model.
  2. Define a Zod schema before importing content, so a missing description or malformed date becomes a build error with a filename rather than a production issue.
  3. Settle slugs, URLs and trailing-slash behavior in config, once.
  4. Write one shared utility per collection that decides what is publishable.
  5. Add required schema, link and metadata checks to the build.
  6. Make each of those checks fail once, deliberately.
  7. Decide which routes need static output and which need request-time data.
  8. Document where shared logic, components and data live.
  9. Keep a redirect map from the first URL change, not the tenth.
  10. Write down who owns each service account, and who gets the billing alert.
  11. Name what triggers publication, and test it separately from the content rules.

If We Started Again

We would choose the content source before designing templates. We would set MDX component rules before publishing the first post. We would centralize publishing eligibility from day one rather than after future-dated content leaked. And we would test every quality gate by forcing it to fail, instead of trusting green checks for months. We would not build the component-routing abstraction until a second genuine use case appeared.

A file-based Astro site can remove much of the recurring plugin work we were used to, including compatibility checks, renewal decisions, theme dependencies and emergency updates for something sitting on the public request path. It does not remove maintenance. It moves it.

Our data

105
commits in the ten weeks after launch

This repository recorded 105 commits in the ten weeks after launch. Twelve involved dependencies or build configuration. The rest covered content, components, and publishing rules.

Garrett Digital repository history, June 1 to August 12, 2026 Verified

Twelve were dependency or build-configuration work, the closest equivalent to the recurring platform maintenance a WordPress team would recognize. The rest is work that would have happened in an admin screen instead of a repository, which is the honest version of the tradeoff: dependencies, deploy credentials, APIs, forms and third-party services all still need review, and none of it disappeared.

Astro is a good fit when separating content, rendering and publishing removes a real constraint, and when someone is prepared to own the workflow that separation creates. Keep WordPress when independent, browser-based publishing is the constraint you do not need to remove.

Planning a New Site or a Rebuild?

We build sites that stay maintainable after launch, on the platform that fits how your team works.