Version pages and regions as code
Goal: get your site’s structure, its pages and its global regions (header, footer), into version control next to your components, and make the repository a place changes can be made, not just a mirror.
The Canvas quickstart pushed components: the building blocks. What editors assemble from them in Drupal Canvas (pages and the regions around them) also pulls, versions, and pushes as JSON specs.
What you’ll have when you’re done
Section titled “What you’ll have when you’re done”- Every Canvas page and global region from your site in the repository, as reviewable JSON.
- A change made in a page spec locally, live on the site after one push.
- External image references in specs resolved to site media with
canvas reconcile-media.
Prerequisites
Section titled “Prerequisites”- A component project connected to your site with working Canvas CLI credentials (Canvas quickstart)
-
Pull the site’s structure
Section titled “Pull the site’s structure”Terminal window npx canvas pullThe scaffold’s
canvas.config.jsonsyncs pages and regions by default (sync.pages,sync.regions), and the pull plan says exactly what’s coming:│ Plan│ Components: 24 pull (24 new)│ Assets: global CSS pull│ Pages: 6 pull (6 new)│ Global regions: 2 pull (2 new)Pages land in
pagesDir(pages/) as one JSON spec per page, named by slug; regions land inregionsDir(regions/header.json,regions/footer.json). Pulling requires thecanvas:page:readandcanvas:page_regionscopes; flag details are in the Canvas CLI reference. -
Read a page spec
Section titled “Read a page spec”// pages/homepage.json (trimmed){"uuid": "20354d7a-e4fe-47af-8ff6-187bca92f3f7","title": "Homepage","path": "/homepage","description": "","elements": {"cc1b44a4-bd8e-4dae-8552-4bc34147b064": {"type": "js.card_container","props": { "layout": "33-33-33", "heading": "What we offer." },"slots": {"content": ["6e6d696c-b0c5-46a7-b072-250ef5295fc1"]}},"6e6d696c-b0c5-46a7-b072-250ef5295fc1": {"type": "js.card","props": { "heading": "Feature or benefit" }}}}elementsis a flat map keyed by element UUID. Each element names its component (type: "js.<machineName>"), its prop values, and, for components with slots, which other elements fill each slot, as an ordered array of UUIDs. Nesting is by reference, not by indentation: the tree is reconstructed from those arrays.Media props carry a
_provenanceentry alongside (target_id: the site’s media entity ID), which is how a spec references an image that lives on the site. -
Make a change locally and push it
Section titled “Make a change locally and push it”Edit a prop in the spec (a heading, a card’s text), then:
Terminal window npx canvas pushThe push plan lists the pages and regions it will update alongside components and CSS. Reload the page on the site: the edit is live. This is the loop that makes the repository authoritative; the browser editor and the codebase edit the same structure. Pushing pages uses the
canvas:page:create/canvas:page:editscopes; regions usecanvas:page_region.A page created only as a local JSON file pushes the same way. Starting from a pulled page’s spec beats writing one from a blank buffer: the editor is the fastest place to compose structure, and the spec format is easiest to get right by example.
-
Edit a global region
Section titled “Edit a global region”Regions are smaller specs with the same element map, plus a
statusflag:regions/footer.json {"status": true,"elements": {"bf4d99cc-0951-4b69-a323-04f21c7d53ce": {"type": "js.footer","props": { "text": "Built with Source CMS" }}}}A region renders on every page: where it appears is fixed by the project’s layout (
src/layout.jsxin the scaffold,<Region name="header" />/<Region name="footer" />), and what it contains is this spec. Edit andcanvas push; the change appears site-wide. -
Reconcile external images
Section titled “Reconcile external images”A spec can reference an image by external URL (an editor pasted one, or a page was composed elsewhere).
canvas validatefails on these:Unreconciled external media URL "https://placehold.co/[email protected]…".Run `canvas reconcile-media` to resolve.Terminal window npx canvas reconcile-mediaThe command uploads each external image to the site as a media entity and rewrites the spec in place:
srcbecomes a site-local file URL, and_provenancerecords the created media entity (target_id) and the originalsource_url. Commit the rewritten specs;validatenow passes and the page no longer depends on a third-party host.
When something goes wrong
Section titled “When something goes wrong”canvas validate fails with Unreconciled external media URL
expected for any external image reference; run npx canvas reconcile-media (step 5).
A slot edit made components vanish from a page
the arrays under slots are the tree. A UUID removed from a slot array orphans that element (and its own children). Restore the UUID, or delete the orphaned elements too.
Push succeeds but the page looks unchanged
check you edited the page the site actually serves; path in the spec is the routing truth, and two specs can have similar titles.
A pulled page references media you don’t have locally
that’s fine. Specs reference media by site-side target_id; nothing image-related needs to exist in the repository.
Next steps
Section titled “Next steps”- Push to a live site: what a push does to a site editors are already using, and how to roll one back.
- Render CMS content with content templates: the same authoring loop, for content-driven rendering.
- Canvas CLI & schema reference: every command and flag this guide used.
Was this page helpful?
What went wrong?
Still stuck? Contact Acquia Support (opens in a new tab)