Skip to content

AI page building

Describe a page or a section and have it built from your approved components, with your real content. Ships as the optional canvas_builder_ai submodule and mounts as a Create tab in the Canvas editor's right panel.

Nothing generates markup. The AI picks from a governed catalogue of the components you already have, and every plan it produces is validated server-side before a single node reaches the page.

Where it lives

There is no floating launcher — the Build UI is a Create tab, a real peer of the panels you already use.

The Create tab in the Page data panel, alongside Details and Motion, showing the Full page / Single section / Describe segmented control

Nothing selected: Create sits next to Details in the Page data panel.

The Create tab in a selected Section's settings panel, alongside Review, Style, Motion, and Advanced, asking What should change in this section? with a Describe your change field, Quick changes chips, and Available components

A top-level section selected: Create joins Review/Style/Advanced and switches to transform mode.

Land on any Canvas page and Create is there immediately, next to Details in the Page data panel. Select a top-level section and the same tab appears in its settings panel — alongside Review/Style/Advanced — but now it modifies the section instead of creating a new one. Select a nested component (a card in a grid, a slide in a carousel) and the tab disappears, keeping that component's own props uncluttered.

Build a single section — step by step

Describe one section and the AI returns up to three takes, built from your approved components. Four screens, in order:

Single section mode listing the section templates — Hero section, CTA section, Feature grid (selected), Photo gallery, Quick links, Content section — above a Build section button

1. Pick a starting point. Choose a section template, or switch to Describe and type a prompt (with an optional screenshot or mockup).

Generating state: a progress bar and checklist — Understanding your request, Choosing components, Writing content, Assembling the section

2. Generating. A short checklist shows what's happening; this usually takes 20-40 seconds.

Review your section: a wireframe thumbnail, a one-line summary, Template / Components / Styles rows, a Back link, and an Insert this section button

3. Review. Facts only — label, component count, style chips — plus See other options if you asked for more than one take.

The three-up feature grid section built and inserted on the canvas, with a Section inserted confirmation and Undo insert / Build another buttons

4. Inserted. The section lands on the canvas as a draft, with a one-click undo.

It lands after the current selection, or at the end of the page if nothing top-level is selected.

Modify an existing section instead

Select a top-level section and describe a change ("make this more concise", "add a call to action") instead of starting from scratch — the same Describe your change field, plus one-tap transform ideas and a list of Available components to drop straight in. The AI sees the section's current content and is asked to preserve its intent, not replace it wholesale.

Build a full page — step by step

Starts from a blueprint rather than a blank prompt, because a page is a sequence with structure, not one big paragraph of instructions — see Blueprints for what a blueprint actually is.

Templates step: blueprint cards — Campaign Landing Page, Program Landing Page, About Us (selected), Contact / Visit Page — with a Choose a direction list of Story-driven (recommended), Institutional, and Bold landing

1. Templates. Pick one of the shipped blueprints (or switch to Describe and write the page freely). The recommended direction is picked for you — choose another under the template, or change it later from Review.

Review your selections: rows for Template, Direction, Sections, Content sources, and Images, each with an edit pencil; Build settings with Auto build and Guided cards; Advanced expanded with an Anything to emphasize? field; a Build page button

2. Review your selections. Every choice on one screen. Auto builds everything in the background; Guided stops after each section for your approval. Advanced holds an optional emphasis note.

Page plan: a draggable list — 1 Hero, 2 Mission, 3 People (with an Events listing-source dropdown and a Band / List toggle), 4 Values, 5 Call to action — each marked Ready

2a. Sections. The section order the blueprint produces — reorder, drop sections, or override how a data-driven section (like "People") resolves.

Content sources: a Search pages by title field and a This page (current content) chip marked Current

2b. Content sources. Optionally ground the build in real pages — the AI reuses their actual words and images instead of inventing them.

A full-page build always adds to whatever page you had open — there is no separate "create a new page" step. If that page already has at least one section, clicking Build page stops first with "This page already has content," naming the page and asking you to confirm before it adds more; a blank page skips straight past this. Clicking Build page may then also ask you to confirm anything that looks data-driven, before it starts building:

Confirm data sections: a People card saying This section looks like it comes from a view. Should it be a data listing? with No and Yes buttons

A quick sanity check for any section that looks like it should pull from a View, before the build spends a call on it.

Guided checkpoint: Building your page with a section checklist, the freshly landed Hero on the canvas, and a Ready for review card with regenerate, Modify, Approve, and Skip controls

Guided mode: each section lands on the canvas, then waits for Approve, Modify (with a note), regenerate, or Skip.

Your page is ready: Hero, Mission, People, Values, and Call to action each marked Inserted, an Undo entire build link, and a Done button

4. Done. Every section's status, plus a single Undo entire build that restores the pre-build page exactly.

Auto skips the per-section checkpoints and runs straight through the same sequence — useful once you trust the plan from Review and just want the page built.

Blueprints

A blueprint is a page recipe — a config entity, exportable and diffable like any other. Eight ship in-box: About us, Campaign landing, Contact & visit, Department page, Event landing, Program landing, Cinematic and Cinematic Long-form.

Each section is generated on its own

A build makes one model call per section, so a section's guidance knows nothing about what any other section produced. Guidance phrased as "if the hero did not already take the animation" or "use a different image from the gallery" cannot be evaluated and is answered optimistically every time. Write budgets as fixed assignments ("the opening statement takes the reveal; the turn statement never animates"), and enforce anything that genuinely needs cross-section state in code. Two such rules ship today: the per-build image ledger, so no photo appears in two sections, and the per-build band rhythm, which drops a section's background colour when it repeats the section before it — asking for alternation in prose produced three identical tinted bands in a row, because no section could see its neighbour's choice.

The portability rule is the important part:

Blueprints never name your components

A blueprint declares abstract capabilitieshero, card_grid, stats, faq, cta, people — not SDC machine names. A resolver maps each capability to whatever your site's approved component set can actually provide. The same blueprint works on any site, with any theme, with any component library.

Blueprint
├── category        selection grouping
├── best_for[]      hints that match a request to this blueprint
├── sections[]      the section pool — the hard structure
│   ├── intent              what the section is for
│   ├── capabilities[]      semantic archetypes, from an extensible registry
│   ├── fallback_capabilities[]
│   └── optional            may be pruned if it does not fit the request
└── variations[]    ordered arrangements + the soft steering
    ├── tone        e.g. "Urgent, action-first"
    ├── emphasis    how to build this variation
    └── section_order[]

Sections are the hard rules; variations are the steering. A variation draws from the blueprint's own section pool — it can reorder and omit, but never invent a new section. One call-to-action may echo earlier in the order, which is how "CTA early and often" landing pages work without opening the door to arbitrary duplication.

In the Review screenshot above, "About Us / Story-driven" is the blueprint and the variation the Choose step auto-picked — the Change link reopens the variation picker without losing your content-source choice, and the Page plan list below it is exactly that variation's section_order, rendered as a draggable, editable list rather than a config file.

Capability resolution

Each section resolves against your approved components and reports a status the Review step shows you before anything is built:

Status Meaning
🟢 Green A primary capability matched a real component.
🟡 Amber Resolved through a fallback, or a custom one-off — best effort.
🔴 Red Nothing on this site can build it.

Capabilities come from a registry, not a hardcoded list — adding one is a config entry plus an optional hook, not a core change.

Content

The full page build's Content step (screenshot above) decides what the copy is made of:

  • AI copy — written for the page.
  • Placeholder — structure now, words later.
  • Site name & tagline — the safest real-content source.

Grounding goes further: pick published pages as reference material and the build uses their actual text and images. The picker searches your pages, and one tap adds the page you are editing.

Two things worth knowing about grounding:

  • The client sends only references, never content. The server resolves them, so grounding stays server-authoritative.
  • Grounded material is framed to the model explicitly as data, not instructions, so nothing a referenced page happens to say can redirect the build.

Images are found by walking entity reference fields down to a media entity with a populated image field. A plain image field directly on the node, or a media item only embedded inline in a text field, is not visible to that walk.

Where the AI can put an image

Two places, and the difference matters:

  • A component's own image prop — a card's picture, a split band's media column. The model writes the url into inputs; an object-typed prop takes {"src": "<url>"}.
  • Any component's background — the background_image style. This is what lets a plain Section become a full-bleed banner without depending on a hero component being installed, so a cinematic opening is composed from Section + background image + overlay tint + a text component rather than from one big SDC. The model is told to always pair it with the overlay tint, because that is what keeps a headline readable over a photograph.

Background video is deliberately not offered to the model: nothing grounds it with real video urls, so the key could only produce invented ones. Editors set video backgrounds themselves.

A build only ever places images the server handed it in that section's AVAILABLE IMAGES block, and no image appears in more than one section of a page — the per-build ledger excludes anything an earlier section already took, and the stock provider is paged deeper to keep later sections supplied.

Stock photo providers

Real photography comes from a pluggable provider subsystem. Unsplash and Pexels ship with the module; each stays inactive until you add its API key on the AI settings page, and with none configured a build simply gets no AVAILABLE IMAGES block rather than failing.

The model never talks to a provider. The server searches, picks candidates for the section it is about to build, and hands over a fixed list — which is what makes the "only places images the server handed it" rule enforceable rather than a request in the prompt.

Adding another source is a plugin: extend StockPhotoProviderBase, declare the credentials it needs, and it appears alongside the shipped two.

Motion

With canvas_builder_motion enabled, a build can also author the section's timeline — which elements move, in what order, and on what trigger.

The vocabulary is not a list the AI module keeps: it is read from the motion submodule, so an effect you disable, or one a target cannot support, is not something the model can propose. Timelines are written through the motion module's public API, which validates every target and action and rejects a bad plan rather than persisting it.

With the submodule disabled, nothing here applies and no motion keys are produced.

Governance

This is the part that makes the feature safe to turn on.

  • An allowlist, not the whole catalogue. Only components you approve can be used.
  • Server-side validation. Every plan is validated against the real component schemas before it becomes page content. An invalid plan is rejected, not repaired into something plausible.
  • Structural guardrails. Caps on sections per page, components per page, and slot nesting depth.
  • Two-layer kill switch. The feature can be disabled wholesale, and each workflow (section transform, content grounding) toggles independently.
  • Placeholder URLs are stripped. Stock-photo placeholder services are rejected as fake content rather than silently shipped.
  • Permissions. Building requires the use canvas_builder ai permission plus update access on the page itself, and every request carries a CSRF header token.

Cost

Token usage is recorded per generation and surfaced on a usage dashboard, with per-model input/output pricing configurable in settings. A full-page build is one model call per section, not one call for the page.

AI usage dashboard: build stats, estimated time savings, total tokens in/out, total estimated cost, and a per-build log with tokens and est. cost per row

Configuration → Content authoring → Canvas Builder → Usage — every generation logged, filterable by action and date, with a per-model cost estimate.

Setup

The submodule hard-depends on three contrib projects, none of which the root composer require pulls in — they are suggest-only so a styling-only site never carries the AI stack:

composer require drupal/ai drupal/ai_agents drupal/key
drush en canvas_builder_ai

Then, in order:

  1. Configure a provider in the AI module.
  2. Turn the feature on — canvas_builder_ai.settings: enabled ships as false, so the panel stays hidden until you enable it under Canvas Builder → AI.
  3. Approve the components the generator may use.
  4. Grant use canvas_builder ai to the roles that should see the panel.

Two operation types matter:

  • chat_with_tools — the section-create path, which uses native function calling. It must be a tools-capable model.
  • chat_with_structured_response — everything else.

Check your configured models are current

A provider default pointing at a retired model fails at invoke time with an error that does not obviously name the cause. If generation fails immediately and consistently, verify the model ids in the AI module's settings before looking anywhere else.

Stock photography (optional). Add an Unsplash or Pexels API key on the AI settings page to let builds place real imagery. Without one, sections are built without photographs rather than with invented urls.

Config keys without an admin form

The settings page covers the rest; these are drush config:set only:

Key Purpose Default
canvas_builder_ai.settings: generation.max_prompt_length Longest editor prompt accepted, in characters. 2000
canvas_builder_ai.settings: generation.http_timeout Provider HTTP timeout, in seconds. 60
canvas_builder_ai.settings: guardrails.max_slot_depth Deepest slot nesting a generated plan may use. 5
canvas_builder_ai.settings: pricing.models Per-model rate rows ({model, input, output, cached}) that win over the synced OpenRouter rates and pricing.default. { }
canvas_builder_ai.components: components.N.allowed_props / allowed_slots / workflows / capabilities Per-component allowlists; empty means everything. The components form carries them through unchanged. (absent)
drush config:set canvas_builder_ai.settings generation.http_timeout 120

Full contracts, endpoints and extension points: modules/canvas_builder_ai/docs/AI.md and modules/canvas_builder_ai/docs/SECTION-BLUEPRINTS.md.