Skip to content

Page builder ​

A Page builder field (html-page) holds a whole page: an ordered stack of blocks (a hero, a feature grid, a FAQ, a call to action) composed visually in the dashboard.

Unlike every other field, it gives you back three things: the rendered HTML, the stylesheet it needs, and the block tree it was rendered from, in case you would rather map it onto your own components.

The HTML is produced when you read it, never stored. Two consequences worth knowing:

  • When a block's design is improved, every page already saved picks it up. You do not re-edit anything.
  • What reaches your site is generated from a fixed catalogue of blocks. It can never contain scripts, event handlers or javascript: URLs, whatever was typed into the editor.

What the API returns ​

json
{
  "id": "gKqv6dx6pBk9",
  "type": "resource",
  "attributes": {
    "title": "Home",
    "body": {
      "html": "<section class=\"dg-block bg-surface\">…</section>",
      "blocks": [
        {
          "id": "0f1c…",
          "type": "hero",
          "props": {
            "title": "Deliver your content everywhere",
            "subtitle": "A headless CMS with no lock-in.",
            "layout": "centered",
            "cta_label": "Get started",
            "cta_url": "https://example.com/signup"
          }
        }
      ],
      "css": ".dg-page{--dg-accent:#8931da…}.py-16{padding-top:4rem;padding-bottom:4rem}…"
    }
  }
}
KeyWhat it is
htmlThe rendered page. Insert it as-is.
blocksThe tree it came from, if you want to render it yourself.
cssExactly the rules this page's markup needs. Usually 3–6 KB.

An untouched page field returns null, like any other empty field.

Using it in Astro ​

The html is styled with utility classes, and your build cannot see class names that arrive from an API. So Diggama generates the CSS for you and ships it with the page: drop css into a <style> and you are done. Nothing to link, nothing to configure, and no class left without rules.

astro
---
// src/pages/[slug].astro
const response = await fetch(`https://api.diggama.com/v2/resources/pages/${Astro.params.slug}`, {
  headers: { Authorization: `Bearer ${import.meta.env.DIGGAMA_TOKEN}` },
});

const { data } = await response.json();
const page = data.attributes.body;
---

<html lang="en">
  <head>
    <title>{data.attributes.title}</title>
    {page && <style set:html={page.css} />}
  </head>
  <body>
    {page && <Fragment set:html={page.html} />}
  </body>
</html>

Because pages are static once rendered, this works just as well at build time in getStaticPaths as it does on demand.

The CSS is per page, not shared

It holds only the rules that page uses, so it stays small, and it carries no reset: your own site's styles are untouched. It also means there is nothing to invalidate: the CSS always matches the HTML it came with.

Rendering the blocks yourself ​

If you want full control (your own components, your own design system), ignore html and walk blocks. Every block is { id, type, props }, and props matches the block reference below.

astro
---
import Hero from '../components/Hero.astro';
import Features from '../components/Features.astro';

const components = { hero: Hero, features: Features };
---

{page.blocks.map((block) => {
  const Component = components[block.type];

  return Component ? <Component {...block.props} /> : null;
})}

Skip the types you have no component for, as in the example above: it lets an editor add a new block without breaking your build.

Importing an existing page ​

If a page already exists, hand-written or generated by an assistant, import it rather than rebuilding it. Click Import HTML above the builder: the dialog has two tabs.

Paste HTML ​

Paste HTML splits the markup into one block per top-level section, which you can then reorder, delete or replace like any other.

The markup keeps its classes and gets real styles: Diggama compiles the CSS for whatever classes it finds, so a page written against the Tailwind CDN renders correctly with no CDN involved. A <style> the paste carried is kept too, scoped to the page so it cannot reach the rest of your site.

What does not come across is anything executable: scripts, event handlers, iframes and forms are removed, and the builder tells you which it dropped.

A pasted section is frozen content: it renders exactly as written, but it has no per-field editing and does not follow the page theme. Rebuild it as a real block when you want those.

Upload a .zip ​

Pages generated elsewhere often come as an index.html next to an assets/ folder. Pasted alone, their images would have nowhere to load from, so upload the whole folder as a .zip instead.

The archive is unpacked in your browser. Diggama imports the shallowest index.html (or the only .html file if there is no index.html), and every image the page references (<img>, srcset, video poster, url() in inline or linked CSS) is uploaded to the media library, resized and served from the CDN. The HTML is imported with its paths rewritten to those CDN URLs, then split into sections as with a paste. The dialog lists anything that could not come along.

LimitValue
Archive size50 MB
Unpacked size200 MB, 1,000 files
Each image10 MB

SVG images are accepted; scripts, event handlers, embedded HTML and external references are stripped from them before they are stored.

Block reference ​

Every block has an id (stable across reorders), a type, and props. In the dashboard, Add a block shows each of them rendered with sample content, and clicking any text on the canvas edits it in place.

Hero ​

hero: Hero ​

PropType
eyebrowstringSmall line above the title.
titlestringRequired.
subtitlestring
imagestring (URL)
layoutcentered | split
cta_labelstring
cta_urlstring (URL)
secondary_cta_labelstring
secondary_cta_urlstring (URL)
notestringUnder the buttons: a reassurance, a price, a date.

hero-image: Hero with background ​

PropType
imagestring (URL)Required.
eyebrowstring
titlestringRequired.
subtitlestring
cta_labelstring
cta_urlstring (URL)
heightshort | tall

Content ​

features: Features ​

PropType
eyebrowstring
titlestring
introstring
columns2 | 3 | 4
stylecards | plain
itemsarray of {icon, title, text}

feature-split: Text and image ​

PropType
eyebrowstring
titlestringRequired.
textstring
imagestring (URL)
sideright | left
pointsarray of {text}
cta_labelstring
cta_urlstring (URL)

steps: Steps ​

PropType
eyebrowstring
titlestring
introstring
itemsarray of {title, text}

rich-text: Text ​

PropType
htmlstring (HTML)Required.

faq: FAQ ​

PropType
eyebrowstring
titlestring
introstring
itemsarray of {question, answer}
footer_textstring
footer_cta_labelstring
footer_cta_urlstring (URL)

Media ​

image: Image ​

PropType
srcstring (URL)Required.
altstringDescribes the image to screen readers.
captionstring
widthcontent | wide | full
roundedboolean
PropType
eyebrowstring
titlestring
columns2 | 3 | 4
shapesquare | landscape
itemsarray of {image, alt, caption}

Social proof ​

logos: Logos ​

PropType
titlestring
borderedboolean
itemsarray of {image, name, url}

stats: Stats ​

PropType
titlestring
itemsarray of {value, label}

testimonial: Testimonial ​

PropType
quotestringRequired.
authorstring
rolestring
avatarstring (URL)
logostring (URL)
toneplain | surface

testimonials: Testimonials ​

PropType
titlestring
columns2 | 3
itemsarray of {quote, author, role, avatar}

team: Team ​

PropType
eyebrowstring
titlestring
introstring
itemsarray of {name, role, photo, url}

Conversion ​

PropType
textstringRequired.
cta_labelstring
cta_urlstring (URL)
toneaccent | dark | plain

cta: Call to action ​

PropType
eyebrowstring
titlestringRequired.
textstring
cta_labelstring
cta_urlstring (URL)
secondary_cta_labelstring
secondary_cta_urlstring (URL)
notestring
toneplain | accent | dark

pricing: Pricing ​

PropType
eyebrowstring
titlestring
introstring
itemsarray of {name, price, period, description, features, cta_label, cta_url, badge, featured}

newsletter: Newsletter ​

PropType
titlestringRequired.
textstring
cta_labelstring
cta_urlstring (URL)Where the button goes: your own signup page or form.
notestring

Layout ​

divider: Divider ​

PropType
spacingsm | md | lg
ruleboolean

embed: HTML ​

PropType
htmlstring (HTML)Required. Click any text on the canvas to edit it directly. Scripts, event handlers and anything that could escape the block are removed.
cssstring (CSS)Only needed for styles that are not utility classes. Scoped to this block.

Writing a page through the API ​

POST and PUT accept the tree, not HTML:

json
{
  "title": "Home",
  "body": {
    "version": 1,
    "blocks": [
      { "id": "hero-1", "type": "hero", "props": { "title": "Hello" } }
    ]
  }
}

The tree is validated: an unknown type, a missing required prop, more than 200 blocks, or an image path outside your project are all rejected with a 422. id is yours to choose: it only has to be unique within the page.

Theming a page ​

A page may override the tokens every block resolves its colors and radii through. They travel with the HTML, on the .dg-page wrapper, including for blocks you did not write.

TokenExample
accent, accent-soft, on-accent#8931da
surface, muted, line#fcfcfd
ink-900, ink-700, ink-500#18181b
radius12px
fontInter, sans-serif
json
{ "version": 1, "theme": { "accent": "#ff0055", "radius": "16px" }, "blocks": [ … ] }

Models ​

When a page field is empty, the editor offers to start from a model: a pre-filled stack of blocks you then edit like any other page. Models are a starting point only: once applied, nothing links the page back to them.

Diggama Documentation