Canopy Blocks let you build entire page sections as reusable templates. Developers define what a section can look like; content editors add blocks to a page, fill in their settings, and arrange them in the Canopy editor. This keeps a clear separation between code and content.
Canopy Blocks replace the deprecated cms_* Canopy elements (cms_text, cms_heading, cms_image, etc.). Always use block templates for new work.
Quick Start
Block templates are single .canvas files in views/canopy/. A minimal block:
{% canopy config %}
{
"title": "Hero",
"settings": [
{ "name": "heading", "type": "heading", "default": { "value": "Welcome", "element": "h1" } },
{ "name": "description", "type": "textarea" }
]
}
{% endcanopy %}
{% canopy template %}
<section>
<{{ settings.heading.element }}>{{ settings.heading.value }}</{{ settings.heading.element }}>
<p>{{ settings.description }}</p>
</section>
{% endcanopy %}
Rendered in a layout via a block area:
<head>{{ canopy.head('main') }}</head>
<body>
{{ canopy.blocks('main') }}
{{ canopy.scripts('main') }}
</body>
Rule Index
Load the rule files that match your task. Load ALL that apply:
| Task | Load |
|---|---|
| Creating/editing a block template, config, or sections; hiding blocks | rules/block-templates.md |
| Defining settings (types, defaults, tabs/groups) | rules/settings.md |
| Rendering blocks in layouts (areas, head/scripts, shared, allow/limit/locked) | rules/rendering.md |
| Parent blocks with child blocks (galleries, sliders, accordions) | rules/nested-blocks.md |
| Designing blocks, build workflow, common patterns | rules/patterns.md |
Maintaining or migrating deprecated cms_* elements |
rules/legacy-elements.md |
This skill is authoritative for Canopy Blocks. Do NOT call the Blutui docs MCP or fetch docs.blutui.com for anything the Rule Index covers — load the matching rule file instead; the rule files are complete and current, including features the public docs may lag behind on. Reach for the docs only for topics genuinely outside this skill (Collections and their field definitions, general Canvas language syntax, dashboard form building) or when a rule file explicitly points there.
Core Rules (Always Apply)
- One block template per
.canvasfile inviews/canopy/— never define blocks anywhere else. The filename becomes the template's name unless the config sets one. - Config must be strictly valid JSON — double quotes, no trailing commas, no comments. Settings with a missing
name, missingtype, or unknowntypeare silently ignored, so a typo fails without an error. - Give every setting a sensible
defaultso blocks look complete the moment an editor adds them. - Guard object and array setting values (
url,file,list,media-sources) with{% if %}/{% for %}before rendering dependent markup. - Always guard
collectionandentrysetting values — deleted content resolves tonull(singleentry,collection) or[](multipleentry select), so unguarded templates break. - Output
richtextvalues with therawfilter — they are stored as HTML. - Pair every block area with
canopy.headandcanopy.scriptsusing the same handle — otherwise blocks' CSS and JS never load. allow,limit, andlockedgo ONLY on thecanopy.blocks()area declaration — never in a block template's config or on a setting.allowlists the templates the area accepts (and is the only way to surface a"hidden": truetemplate),limitcaps the block count,lockedfixes the structure (fields stay editable; add/remove/reorder do not). The only picker control in block config ishidden.- Nested blocks: every parent template needs exactly one plain
{{ canopy.children() }}call (filtered/indexed/childList renders are display-only — without the plain call editors cannot add or manage children), and every child template's config needs an explicit"name"so it registers under the key filters anddefaultlists expect. - Choose the right tool: blocks for editor-composed sections;
{ shared: true }areas for global sections (footers); Collections for structured, queryable data (blog posts, products) — surfaced in blocks via thecollectionandentrysetting types; forms via theformsetting type plus the{% form %}tag. - Never use deprecated
cms_*element functions in new work — build a block template instead.