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
Open the CMS tab in the left sidebar.
Click New Collection. A modal appears asking for the collection name and schema fields.
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.Add fields one by one. For each field, choose a type, a label, and whether it is required. See the field types table below.
Set the Slug Field -- the field whose value becomes the URL slug for each item (commonly "slug" or "title").
Set the URL Pattern -- the route template for individual items, e.g.
/blog/{{slug}}.Click Create. The collection is saved as part of a template page in
pages/templates/.
CMS Field Types
Type | Description |
|---|---|
| Single-line text input |
| Multi-line textarea |
| HTML rich text editor (stored as Tiptap JSON, rendered as HTML) |
| Numeric value |
| True/false toggle |
| Image file path |
| Any file upload (supports optional accept MIME filter) |
| Date or datetime picker (stored as ISO string) |
| Dropdown from a list of options (supports |
| Link to an item in another collection |
| Internationalized single-line text |
| Internationalized multi-line text |
Every item also receives these auto-generated system fields:
Field | Description |
|---|---|
| Unique identifier (UUID) |
| Stable file name, never changes after creation |
| ISO timestamp of creation |
| ISO timestamp of last update |
| Computed URL path based on the collection's URL pattern |
Adding and Editing Items
In the CMS tab, select a collection from the list.
Click New Item. The CMS field editor appears with inputs matching the schema you defined.
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.
Click Save. The item is written as a JSON file inside
cms/{collection}/.To edit an existing item, click it in the item list. Change any field and save.
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
Go to the Pages tab in the sidebar.
Create a new page inside the
pages/templates/directory (e.g.pages/templates/blog-post.json).In the page settings, set the source to
"cms"and configure the cms object with the collection ID, slug field, and URL pattern.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.
When defining the schema, add a field with type
reference.Set the
collectionoption to the target collection ID (e.g."team").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.jsonEach 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 |
|---|---|---|
|
| List all collections |
|
| Get collection schema |
|
| List items in a collection |
|
| Get a single item |
|
| Create a new item |
|
| Update an item |
|
| 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.jsonfile
Next Steps
Lists -- Display collections of items in any page using List nodes
Internationalization -- Localize CMS content with i18n fields
Filtering and Search -- Add client-side filtering to CMS lists
Deployment -- Build and deploy your CMS-powered site