Stack

A stack is a heterogeneous, ordered list of blocks. Each item in the list picks its own block model — one item can be a hero, the next a quote, the next a CTA — and the editor controls the composition and order. Stacks are the field type to reach for when a page section needs to mix different reusable components in an editor-chosen sequence.

This page covers the model syntax, how stack content is shaped, how it renders, and how stacks fit alongside other structural field types like groups, collections, and block fields.

Note on terminology. Stack is the current composable-blocks field type in SleekCMS. An older concept called "dynamic blocks" has been deprecated — stacks replace it. If you encounter older docs or templates referencing "dynamic blocks," they describe the deprecated field; the current product uses stack.


Model Syntax

A stack is declared with the bare keyword stack — no parentheses, no square brackets. Stacks are always multi-item by definition, so there's no separate "single vs. multiple" toggle.

{
    title: text,
    sections: stack
}

That's the entire configuration. There is no per-field allow-list of block types — any block defined under models/blocks/ is available in any stack. The block library is the menu; the stack draws from all of it.

If you want only one type repeated, that's a different field — use [block(key)]. If you want a one-off shape on a single page, use a collection [{ ... }]. Stacks are specifically for "this section can be one of several block types, in editor-chosen order."

Choosing Between Structural Fields


Content Shape

A stack field's content is an array of objects. Each object is shaped like the block model it instantiates, plus a _block marker carrying that block's key as a plain string:

{
    "title": "About us",
    "sections": [
        {
            "_block": "hero",
            "heading": "Care that feels personal",
            "subheading": "A small team focused on long-term relationships.",
            "background": "pexels:doctor"
        },
        {
            "_block": "cta",
            "label": "Book a visit",
            "link": "/contact"
        },
        {
            "_block": "quote",
            "body": "The most thoughtful clinic I've been to.",
            "attribution": "M. Castillo"
        }
    ]
}

The _block value is the block's key"hero", "cta", "quote" — never a numeric id. The CMS resolves the key to an internal id on save.

The remaining fields on each item must match the block model's schema. A hero item must have the fields declared in models/blocks/hero.model; a cta item must match models/blocks/cta.model. The order of items in the array determines render order.


Rendering

A stack renders through the same render() helper as other block fields. Pass the whole array and render() dispatches per _block, looking up blocks/<key>.ejs for each item:

<h1><%= item.title %></h1>
<%- render(item.sections) %>

Each item flows through its own block template with the item's data exposed as item. The page template doesn't need to know — or branch on — what types of blocks are present; the dispatch is implicit.

An optional separator can be inserted between rendered items:

<%- render(item.sections, '<hr class="divider">') %>

Block ModelsTemplate Context and Data Access


Stacks in the Editor

In the editor, a stack field is presented as a vertical list of block instances. Editors:

  • Add a new block by picking a block model from the full block library.
  • Reorder items by dragging them up or down.
  • Hide an item — the data is kept, but the item is excluded from rendering and from API responses.
  • Remove an item entirely.
  • Edit any item's fields inline.
  • Title an item with an editor-only label so the collapsed list is scannable ("Spring Campaign Hero", "Free Trial CTA"). These titles are UI-only and don't appear in the published output or in API responses.

The editing surface is the same regardless of which block models are present — adding a new block model to your site makes it instantly available in every stack on every model.

Content Editing


Stacks via the Content API

In the content API, stacks come through as the same array shape stored on the record. Each item carries the _block marker and the block's resolved field data:

const page = client.getPage('/about');
// page.sections → [
//   { _block: 'hero', heading: 'Care that feels personal', ... },
//   { _block: 'cta', label: 'Book a visit', link: '/contact' },
//   { _block: 'quote', body: '...', attribution: 'M. Castillo' }
// ]

Your frontend dispatches on _block to pick the right component:

const components = {
  hero: HeroSection,
  cta: CTABanner,
  quote: PullQuote,
};

function Sections({ sections }) {
  return sections.map((item, i) => {
    const Component = components[item._block];
    return Component ? <Component key={i} {...item} /> : null;
  });
}

Image, video, file, reference, and other field types inside each item are resolved the same way they are on any other record — image objects are full { url, raw, alt, ... } objects, references are full entry objects inline, and so on.


Where Stacks Can Live

Stacks can be declared on page models and entry models. They cannot be declared inside a block model — block models cannot contain stacks (or other blocks). This keeps the dispatch tree shallow and the editor predictable.

A stack item, however, may internally contain:

  • Groups ({ ... }) — non-repeatable nested objects.
  • Collections ([{ ... }]) — repeatable inline structures.
  • Entry references (entry(key), [entry(key)]).

It may not contain other stacks or block fields. If you find yourself wanting nested stacks, restructure the parent block to use groups or collections instead.

Block ModelsGroup FieldsCollection Fields


Choosing Between Structural Fields

Stacks sit at one end of a decision tree. The right choice depends on whether the shape is one-off or reusable, whether each item is the same shape or different, and whether the content is local to one place or shared across many.

When the section is… Reach for…
A one-off shape used on one model only A group { ... } (single instance) or [{ ... }] (repeatable)
A reusable shape, every item the same type block(key) (single instance) or [block(key)] (repeatable list of the same block)
A reusable shape, each item can be a different block chosen by the editor stack
Shared content referenced from many records entry(key) or [entry(key)]

The defining property of a stack is heterogeneity: items in the same field can be different block models, picked by the editor at content-edit time. If every item is the same block, prefer [block(key)] — it's a tighter contract for both the editor and the template.

Block FieldsCollection FieldsGroup Fields


A Complete Example

A landing page model with a fixed hero and a stack body:

models/pages/landing.model

{
    title: text,
    hero: block(hero),
    body: stack
}

models/blocks/hero.model

{
    heading: text,
    subheading: paragraph,
    background: image
}

models/blocks/feature_grid.model

{
    heading: text,
    items: [
        {
            icon: image,
            title: text,
            description: paragraph
        }
    ]
}

models/blocks/cta.model

{
    label: text,
    link: link
}

content/pages/landing.json

{
    "title": "New product launch",
    "hero": {
        "heading": "Meet Atlas",
        "subheading": "A new platform for distributed teams.",
        "background": "pexels:office team"
    },
    "body": [
        {
            "_block": "feature_grid",
            "heading": "Built for the way you work",
            "items": [
                { "icon": "iconify:mdi:bolt", "title": "Fast",   "description": "..." },
                { "icon": "iconify:mdi:lock", "title": "Secure", "description": "..." }
            ]
        },
        {
            "_block": "cta",
            "label": "Start free trial",
            "link": "/signup"
        }
    ]
}

pages/landing.ejs

<% title(item.title) %>

<%- render(item.hero) %>

<main>
  <%- render(item.body) %>
</main>

The hero field is a single block(hero) — always present, always a hero, no editor choice. The body field is a stack — editors compose it from any block in the library, in any order they choose.


What's Next