Guides

Content Management System

Meno includes a built-in CMS for managing structured content like blog posts, team members, products, or any repeatable data. Content is stored as JSON files on disk and rendered to static HTML at build time.


Creating a Collection

  1. Open the CMS tab in the left sidebar.

  2. Click New Collection. A modal appears asking for the collection name and schema fields.

  3. Enter a name (e.g. "Blog Posts") and an ID (e.g. "blog"). The ID determines the folder name under cms/ and the URL segment.

  4. Add fields one by one. For each field, choose a type, a label, and whether it is required. See the field types table below.

  5. Set the Slug Field -- the field whose value becomes the URL slug for each item (commonly "slug" or "title").

  6. Set the URL Pattern -- the route template for individual items, e.g. /blog/{{slug}}.

  7. Click Create. The collection is saved as part of a template page in pages/templates/.


CMS Field Types

Type

Description

string

Single-line text input

text

Multi-line textarea

rich-text

HTML rich text editor (stored as Tiptap JSON, rendered as HTML)

number

Numeric value

boolean

True/false toggle

image

Image file path

file

Any file upload (supports optional accept MIME filter)

date

Date or datetime picker (stored as ISO string)

select

Dropdown from a list of options (supports options array and multiple: true)

reference

Link to an item in another collection

i18n

Internationalized single-line text

i18n-text

Internationalized multi-line text

Every item also receives these auto-generated system fields:

Field

Description

_id

Unique identifier (UUID)

_filename

Stable file name, never changes after creation

_createdAt

ISO timestamp of creation

_updatedAt

ISO timestamp of last update

_url

Computed URL path based on the collection's URL pattern


Adding and Editing Items

  1. In the CMS tab, select a collection from the list.

  2. Click New Item. The CMS field editor appears with inputs matching the schema you defined.

  3. Fill in each field. The editor adapts to the field type -- a date picker for date fields, a rich text toolbar for rich-text fields, a file browser for image fields, and so on.

  4. Click Save. The item is written as a JSON file inside cms/{collection}/.

  5. To edit an existing item, click it in the item list. Change any field and save.

  6. To delete an item, click the delete button next to the item.

CSV Import

For bulk content, click the Import button at the top of the item list. Select a CSV file where column headers match your field names. Each row creates one CMS item.


Template Pages

A template page is a regular Meno page stored in pages/templates/. At build time, one HTML file is generated for every item in the linked collection.

Creating a Template Page

  1. Go to the Pages tab in the sidebar.

  2. Create a new page inside the pages/templates/ directory (e.g. pages/templates/blog-post.json).

  3. In the page settings, set the source to "cms" and configure the cms object with the collection ID, slug field, and URL pattern.

  4. Design the page layout. Wherever you want to display item data, use the template expression {{cms.fieldName}}.

For example, to show the title and body of a blog post:

  • Add a heading element. Set its text content to {{cms.title}}.

  • Add a div element. Set its content to {{cms.body}}.

The {{cms.*}} expressions are replaced with each item's actual values at build time.


Reference Fields

Reference fields link items across collections. For example, a blog post can reference an author from a "team" collection.

  1. When defining the schema, add a field with type reference.

  2. Set the collection option to the target collection ID (e.g. "team").

  3. To allow selecting multiple items, set multiple: true.

In templates, access referenced item data with dot notation:

{{cms.author.name}}
{{cms.author.avatar}}

Inside a List node (see the Lists guide), use {{item.author.name}} or the named context variable like {{post.author.name}}.


File Structure

project/
  pages/
    templates/
      blog-post.json       # Template page -- generates /blog/{slug} per item
  cms/
    blog/                  # Collection data folder
      my-first-post.json   # Individual item
      another-post.json
    team/
      jane-doe.json

Each item file in cms/{collection}/ contains the field values for that item along with system fields (_id, _filename, _createdAt, _updatedAt).


API Endpoints

When running the dev server, CMS data is available through these endpoints:

Method

Route

Description

GET

/api/cms/collections

List all collections

GET

/api/cms/collections/:id

Get collection schema

GET

/api/cms/:collection

List items in a collection

GET

/api/cms/:collection/:slug

Get a single item

POST

/api/cms/:collection

Create a new item

PUT

/api/cms/:collection/:slug

Update an item

DELETE

/api/cms/:collection/:slug

Delete an item


Under the Hood

Schema Definition in JSON

The CMS schema is embedded inside the template page's meta object. This is the single source of truth for the collection structure.

{
  "meta": {
    "title": "Blog Post",
    "source": "cms",
    "cms": {
      "id": "blog",
      "name": "Blog Posts",
      "slugField": "slug",
      "urlPattern": "/blog/{{slug}}",
      "fields": {
        "title": {
          "type": "string",
          "required": true,
          "label": "Title"
        },
        "slug": {
          "type": "string",
          "required": true,
          "label": "URL Slug"
        },
        "body": {
          "type": "rich-text",
          "label": "Content"
        },
        "category": {
          "type": "select",
          "options": ["tech", "design", "business"],
          "label": "Category"
        },
        "author": {
          "type": "reference",
          "collection": "team",
          "label": "Author"
        },
        "published": {
          "type": "boolean",
          "default": false,
          "label": "Published"
        },
        "publishDate": {
          "type": "date",
          "label": "Publish Date"
        },
        "cover": {
          "type": "image",
          "label": "Cover Image"
        }
      }
    }
  },
  "body": [
    {
      "type": "node",
      "tag": "article",
      "children": [
        {
          "type": "node",
          "tag": "h1",
          "children": "{{cms.title}}"
        },
        {
          "type": "node",
          "tag": "div",
          "children": "{{cms.body}}"
        }
      ]
    }
  ]
}

Important: The cms object must be nested inside meta, not at the top level of the page JSON. The source field must be set to "cms".

Item JSON

Each item stored in cms/blog/my-first-post.json:

{
  "_id": "a1b2c3d4-e5f6-7890-abcd-ef1234567890",
  "_filename": "my-first-post",
  "_createdAt": "2025-01-15T10:30:00Z",
  "_updatedAt": "2025-01-20T14:00:00Z",
  "title": "My First Post",
  "slug": "my-first-post",
  "body": "<p>Hello world.</p>",
  "category": "tech",
  "author": "jane-doe",
  "published": true,
  "publishDate": "2025-01-15",
  "cover": "/uploads/cover.jpg"
}

Client-Side Data

For dynamic client-side filtering, you can configure the clientData option in the schema:

{
  "cms": {
    "clientData": {
      "enabled": true,
      "strategy": "auto",
      "threshold": 500,
      "fields": ["title", "slug", "category", "publishDate"]
    }
  }
}

Strategies:

  • "auto" -- inline JSON for small collections, separate JSON file for large ones

  • "inline" -- embed JSON in the HTML (best for fewer than 500 items)

  • "static" -- generate a separate /data/{collection}/index.json file


Next Steps

Building the future of digital experiences, one website at a time

© 2026 Company. All rights reserved.