Collection

A collection field is a repeatable group of fields — a structure where editors can add multiple items, each with the same set of sub-fields. Think of it as an inline list where every item has a defined shape. An FAQ section with question-answer pairs, a feature list with icon-title-description rows, a timeline with date-event entries — these are collections. The structure is defined once in the model, and editors create as many items as they need.

This page covers how collections work, the editing experience for adding and managing items, how collection data appears in the API and templates, nesting collections and groups, and how to decide between collections and other structural field types.


What Is a Collection Field

A collection field defines a repeatable set of sub-fields within a model. Each item in the collection has the same field structure, but editors control how many items exist. A model with a collection field is saying: "this content has a variable-length list of structured items."

When you add a collection field to a model, you configure:

A name and handle — The name is what editors see as the section heading in the editor. The handle determines the key name in the API response where the array of items is stored.

Sub-fields — The fields that make up each item. Any field type can be used inside a collection — text, images, numbers, dropdowns, references, groups, even other collections. You define the fields once, and every item in the collection has that same structure.

The result is an array of structured objects. A "Features" collection with sub-fields for icon, title, and description produces an array where each element has those three fields. Editors add items to the array, fill in the sub-fields for each one, and reorder or remove items as needed.

Collections are defined inline within the model they belong to. Unlike blocks, which are defined as separate models and can be shared across many page and entry models, a collection's field structure is specific to its parent model. This makes collections simpler to set up — there's no separate model to create — but it means the structure can't be reused elsewhere.


Common Use Cases

Collections appear wherever content naturally takes the form of a list with a consistent structure. A few patterns that come up frequently:

FAQ lists — A collection with question and answer fields. Editors add one item per FAQ entry, and the result is an array of Q&A pairs that your frontend renders as an accordion, a list, or any layout you choose.

Feature lists — A collection with icon, title, and description fields. Each item represents a feature, benefit, or selling point. This is common on landing pages, product pages, and pricing sections.

Social links — A collection with icon and URL fields. Each item represents a social media profile. This is a typical pattern on footer or header entry models where the number of social links varies per site.

Timeline or history entries — A collection with date, title, and description fields. Each item is a milestone, event, or historical entry displayed in chronological order.

Team member highlights — A collection with name, role, photo, and bio fields on an "About" page model, when team members don't need their own URLs or entry records. If team members are referenced from multiple places, an entry model is a better fit.

Navigation items — A collection with label, URL, and optional icon fields on a navigation entry model. Each item is a menu link. Nested navigation can be modeled with collections inside collections.

Pricing tiers — A collection with plan name, price, description, and features sub-fields. Each item is a tier on a pricing table.

Testimonials or quotes — A collection with quote text, author name, author title, and company fields. Each item is a testimonial that renders as a card, carousel slide, or list item.

The unifying characteristic is a variable number of items with a consistent structure. If every item has the same fields and editors need to control how many items exist, a collection is the right tool.


The Editor Experience

The collection editor presents items as a vertical list where each item can be expanded to reveal its sub-fields. Editors interact with collections through a straightforward set of actions.

Adding Items

Editors add a new item to the collection and fill in its sub-fields. The new item appears at the end of the list with empty fields ready for content. There's no limit on how many items an editor can add — a collection grows as needed.

Reordering Items

Items can be reordered by dragging them to a new position. If the fifth FAQ entry should be first, the editor drags it to the top. The order of items in the editor is the order they appear in the API response and in templates.

Removing Items

Editors can remove an item from the collection. This deletes the item and its content permanently. Unlike dynamic blocks, there is no "hide" action — removing is the only way to exclude an item.

Inline Editing

Each item's sub-fields are edited directly within the collection interface. Editors expand an item to see its fields, make changes, and collapse it. There's no separate editing screen or modal — everything happens inline within the parent model's editor.

For collections with many sub-fields per item, the interface keeps things manageable through collapsible item sections. Editors can collapse items they've already completed and focus on the one they're currently editing.


Data Structure in the API

A collection field produces an array of objects in the API response. Each object in the array contains the sub-fields defined for that collection, and the array order matches the order editors set in the editor.

A page model with a "Features" collection containing icon, title, and description sub-fields produces this shape:

{
  "title": "Our Product",
  "features": [
    {
      "icon": "⚡",
      "title": "Lightning Fast",
      "description": "Built for speed from the ground up."
    },
    {
      "icon": "🔒",
      "title": "Secure by Default",
      "description": "Enterprise-grade security out of the box."
    },
    {
      "icon": "🔌",
      "title": "Easy Integration",
      "description": "Connect to your existing tools in minutes."
    }
  ]
}

The features array has three items because the editor added three items. A different page instance using the same model might have five items or one. The structure of each item is identical — icon, title, description — but the number of items varies per record.

Empty collections return an empty array. If an editor creates a page but doesn't add any items to the collection, the API returns "features": [].

Nested Structures

Collections can contain groups, and groups can contain collections. This lets you model more complex data structures when items have sub-sections of their own.

A "Pricing Tiers" collection might contain a group for "Plan Details" (name, price, billing period) and a nested collection for "Included Features" (feature name, included boolean). The API response reflects the nesting:

{
  "pricing_tiers": [
    {
      "plan": {
        "name": "Starter",
        "price": 9,
        "period": "month"
      },
      "included_features": [
        { "feature": "5 projects", "included": true },
        { "feature": "API access", "included": false }
      ]
    },
    {
      "plan": {
        "name": "Pro",
        "price": 29,
        "period": "month"
      },
      "included_features": [
        { "feature": "Unlimited projects", "included": true },
        { "feature": "API access", "included": true }
      ]
    }
  ]
}

Nesting is powerful but adds editorial complexity. A collection inside a collection means editors are managing lists within lists. Keep nesting shallow — one level of nesting is usually sufficient. If the structure gets deeper, consider whether blocks or entry references would simplify the editing experience.


Collections in Templates

In the site builder, collections are accessed through the item variable and iterated with standard EJS loops. The template receives the collection as an array and renders each item.

Basic Iteration

A features collection rendered as a grid:

<div class="features-grid">
  <% item.features.forEach(feature => { %>
    <div class="feature-card">
      <span class="feature-icon"><%= feature.icon %></span>
      <h3><%= feature.title %></h3>
      <p><%= feature.description %></p>
    </div>
  <% }); %>
</div>

FAQ Accordion

A collection of question-answer pairs rendered as an accordion:

<div class="faq-list">
  <% item.faqs.forEach((faq, index) => { %>
    <details>
      <summary><%= faq.question %></summary>
      <div class="faq-answer"><%- faq.answer %></div>
    </details>
  <% }); %>
</div>

Social Links

A collection of social media links rendered in a footer:

<div class="social-links">
  <% item.socials.forEach(social => { %>
    <a href="<%= social.link %>" aria-label="Social media">
      <i class="<%= social.icon %>"></i>
    </a>
  <% }); %>
</div>

Conditional Rendering

Check whether the collection has items before rendering its container:

<% if (item.features && item.features.length > 0) { %>
  <section class="features">
    <h2>Features</h2>
    <% item.features.forEach(feature => { %>
      <div class="feature"><%= feature.title %></div>
    <% }); %>
  </section>
<% } %>

Collections in the Content API

When consuming collections through the @sleekcms/client, you access them as arrays on the page or entry object. Your frontend iterates over the array and renders the appropriate UI for each item.

const page = client.getPage('/pricing');

// Iterate over a collection
page.pricing_tiers.forEach(tier => {
  console.log(tier.plan.name, tier.plan.price);
  tier.included_features.forEach(f => {
    console.log(` ${f.feature}: ${f.included ? '✓' : '✗'}`);
  });
});

In a React application, collections map naturally to list rendering:

function FeatureList({ features }) {
  return (
    <div className="features-grid">
      {features.map((feature, index) => (
        <div key={index} className="feature-card">
          <span>{feature.icon}</span>
          <h3>{feature.title}</h3>
          <p>{feature.description}</p>
        </div>
      ))}
    </div>
  );
}

// Usage
const page = await client.getPage('/product');
<FeatureList features={page.features} />

Content API@sleekcms/client


When to Use Collection vs Block

Collections and blocks both produce arrays of structured objects, but they differ in scope, reusability, and the editor experience. Choosing between them depends on whether the repeating structure is local to one model or shared across many.

Use a collection when:

  • The repeating structure is specific to one model. An FAQ list on a product page model, a list of social links on a footer entry — these don't need to be shared across other models.
  • The items are simple and uniform. Each item has the same few fields, and there's no need for different item types within the same list.
  • You want a lightweight inline editing experience. Collections don't require creating a separate model — the sub-fields are defined directly within the parent model.
  • The rendering is straightforward. All items render the same way because they all have the same structure.

Use a block (via dynamic block fields) when:

  • The repeating structure is used across multiple models. A CTA section that appears on landing pages, blog posts, and product pages should be a block — define it once, reuse it everywhere.
  • Editors need to mix different item types in the same list. A dynamic block field lets editors choose from hero blocks, feature blocks, testimonial blocks, and CTA blocks in any combination. A collection only supports one item structure.
  • Each item type has its own visual template. Blocks get their own EJS templates in the site builder, making rendering modular. Collection items are all rendered by the same template logic.
  • You want structural changes to propagate. Modifying a block model updates every instance across the site. Modifying a collection's sub-fields only affects the model where it's defined.
Capability Collection Block (Dynamic)
Defined in Inline within parent model Separate block model
Reusable across models No Yes
Multiple item types in same list No Yes
Own template in site builder No Yes
Structural changes propagate No (local to model) Yes (site-wide)
Editor complexity Lower Higher
Best for Simple repeating lists Composable, mixed-type sections

The distinction often comes down to a practical question: will this exact repeating structure appear in other models? If yes, use a block. If the list is specific to this model and all items look the same, a collection keeps things simple.


When to Use Collection vs Group

Collections and groups both nest sub-fields, but they differ in cardinality. A group produces a single object. A collection produces an array of objects.

Use a group when the data appears exactly once per record. A page has one hero section, one set of SEO fields, one author attribution. There's no "add another" action — the structure is fixed and singular.

Use a collection when editors need a variable number of items. An FAQ page needs multiple question-answer pairs. A footer needs multiple social links. The number of items varies per record and editors control how many exist.

If you're unsure, ask: "Can there be more than one of these?" If yes, it's a collection. If no, it's a group.

Group Fields


What's Next

  • Group Fields — Non-repeatable field containers for organizing related fields.
  • Block Models — Reusable, composable content components for dynamic page sections.
  • Dynamic Blocks — Configuring composable layouts with multiple block types.
  • Content Field Types — The full set of field types available in page, entry, and block models.
  • Page Models — How fields combine to define routable page content.
  • Entry Models — How fields combine to define reusable structured data.