By Jon Toney on Sep 16, 2026

Headless PageCraft with Bones and Astro

Headless PageCraft with Bones and Astro

Editors build pages in PageCraft. Astro and Bootstrap show them. Here is the short version of what that takes, and the five things that can trip you up.

Why do this at all

ExpressionEngine already renders PageCraft pages perfectly well. Splitting the front end off is worth it when you want something EE templates can't easily give you:

  • A faster, cheaper site. Visitors get plain HTML files from a CDN. No PHP, no database, nothing to fall over under traffic.

  • A modern front-end toolkit. Components, type checking, a build step, and any framework you want, instead of EE template tags.

  • Editors keep what they know. They still build pages visually in PageCraft, in the control panel they already use. That is the point of doing this with PageCraft rather than a plain text field: the visual editing survives the move.

  • A smaller target. The control panel doesn't have to sit on the public site at all.

  • One source, several outputs. The same content can feed a site, an app, or a client's own front end.

What you take on in exchange: the site has to be rebuilt when content changes, previewing needs setting up, and a few things PageCraft does for you on EE, such as wiring up forms, become yours to handle. If none of the benefits above are pressing, staying with EE templates is the simpler choice.

How it fits together

  • ExpressionEngine holds the pages. Each one is an entry whose body is a PageCraft field, ordered by Structure.

  • Bones turns that into JSON. One template per endpoint: /api for entries, /api/nav for the Structure navigation.

  • Astro fetches both when you build, and writes one static HTML file per page.

  • The browser still loads PageCraft's stylesheets and scripts from the EE site. That part cannot be made fully independent unless you copy those files across.

What the API gives you, and what it leaves out

A PageCraft field arrives as three strings:

page_builder

{
  "html":       "<div class=\"is-section is-box type-poppins\"> … </div>",
  "mainCss":    "",
  "sectionCss": ".is-section-40 { … }"
}

That is the page, and nothing that makes it look like a page. The markup uses ContentBox classes such as is-section and is-container, which are defined in stylesheets that live on the EE site. Blocks like video backgrounds need PageCraft's JavaScript too, and fonts come from one stylesheet per type-* class in the markup.

So the front end has to load, in this order, what PageCraft loads on its own pages:

  1. ContentBox stylesheetsPageCraft ships .framework versions built for sites that already use Bootstrap or Tailwind. Use those.

  2. Two small fixesBootstrap grid behaviour inside ContentBox rows, and a section-height fix.

  3. The runtime and any pluginsOnly for pages that use blocks like video backgrounds or sliders.

  4. Font stylesheetsOne per type-* class in the markup.

  5. The page's own CSS, then its HTMLWrapped in <div class="is-wrapper">.

In this project that is one Astro component, src/components/PageCraft.astro, and pages use it in a line:

src/pages/[...slug].astro

<PageCraft field={entry.page_builder} />

Five things that trip you up

1. Plugins fail to load across domains

PageCraft loads plugins with a dynamic import, which browsers block across domains unless the server allows it. Your front end is on a different address from EE, so plugins fail silently apart from a console error. On DDEV, allow it for PageCraft's folder only:

.ddev/nginx/astro-frontend.conf

location ^~ /themes/user/pagecraft/ {
    add_header Access-Control-Allow-Origin "*" always;
    try_files $uri =404;
}

If it still fails after a restart, reload the page ignoring the cache. The browser remembers the earlier response.

2. PageCraft restyles some Bootstrap classes

ContentBox's stylesheet loads after Bootstrap, so on any page with PageCraft content, primary buttons turn teal. PageCraft does this on EE's own pages too. Override what you care about in your Sass file.

3. Drafts are invisible

Bones returns only open entries unless you ask for others, so a page saved as a draft simply disappears from your site. That is the right default for a public build, but it means you cannot preview work in progress. If you need that, request drafts in development only.

Worth knowing: anyone can add ?status=draft to the API and read your unpublished pages. Turn on the Bones API key before the site leaves your machine.

4. The home page lives at two addresses

EE serves the home entry at /home/; your front end serves it at /. Anything using the CMS path, including PageCraft's Preview button, lands on a page that doesn't exist. Redirect /home/ to / in your Astro config.

5. Node doesn't trust DDEV's certificate

Builds fail with a certificate error until you point Node at the local certificate authority. The npm scripts do it, which also means they only work on a machine with DDEV.

Previewing from the control panel

PageCraft can point its Preview button at your front end instead of EE. In its settings, set the preview URL to your dev server and the path to /{uri}/, which is the Structure path and handles nested pages.

Two things to remember. Preview shows the last saved version, not what is on screen in the editor. And pointed at a dev server it is current, while pointed at a built site it shows the last build.

Before you add a form

PageCraft's form block stores the recipient's email address in the page's HTML. On EE's own pages PageCraft removes it and wires the form up at the moment it renders. The API does neither, so a headless page would publish that address and the form probably wouldn't deliver anything. Build forms in your front end instead, or check with the developers first.

What's left on this project

  • Turn on the Bones API key

  • Decide whether the API and its action URLs should be reachable publicly

  • Show drafts in development so Preview works on unpublished pages

  • Override the styles ContentBox takes from Bootstrap

  • Rebuild the site automatically when an editor publishes

Based on a working local build: ExpressionEngine on DDEV with Bones and PageCraft, Astro 7 and Bootstrap 5.3. Add-on behaviour was read from the versions installed here and may change.

{journal_authors}Jon Toney{/journal_authors:bio_pic}{/journal_authors Jon Toney Founder + Creative Director

Jon is a brand builder with 25+ years of turning ideas into bold, meaningful brands. Equal parts strategist and designer, he’s helped everyone from BBQ joints to tech giants stand out with clarity, purpose, and a little attitude.

Let’s build something worth talking about.

We are committed to protecting your privacy. We will never collect information about you without your explicit consent.