Reference

Node Types Reference

Meno pages are built from a tree of typed nodes. Every node has a type field that determines its behavior, available properties, and how it renders to HTML during static build.

There are 7 node types: node, component, link, embed, list, locale-list, and slot.


1. node (HTML Element)

Renders a standard HTML element. This is the most common node type.

Properties

Property

Type

Required

Description

type

"node"

Yes

Node type discriminator

tag

string

Yes

HTML tag name (div, span, section, img, etc.)

style

StyleObject

No

Responsive style object with base, tablet, mobile keys

attributes

`Record<string, string | number | boolean>`

No

HTML attributes (id, class, src, alt, data-*, etc.)

children

`string | (string | Node)[]`

No

Text content or array of child nodes and strings

label

string

No

Custom label displayed in the editor structure tree

interactiveStyles

InteractiveStyleRule[]

No

CSS rules for pseudo-states like :hover, :focus

if

`boolean | object | string`

No

Conditional rendering expression

generateElementClass

boolean

No

Generate an element class without any styles (for custom CSS)

Examples

Basic container with padding:

{
  "type": "node",
  "tag": "div",
  "style": { "base": { "padding": "16px", "display": "flex", "gap": "12px" } },
  "children": [
    { "type": "node", "tag": "span", "children": "Hello" },
    { "type": "node", "tag": "span", "children": "World" }
  ]
}

Image element (void element, no children):

{
  "type": "node",
  "tag": "img",
  "attributes": {
    "src": "/hero.jpg",
    "alt": "Hero image",
    "fetchpriority": "high",
    "loading": "eager",
    "width": "1200",
    "height": "600"
  }
}

Text content does NOT use a text property. Always use children:

{
  "type": "node",
  "tag": "p",
  "children": "This is a paragraph."
}

Mixed text and inline elements:

{
  "type": "node",
  "tag": "p",
  "children": [
    "Read our ",
    { "type": "node", "tag": "strong", "children": "documentation" },
    " to learn more."
  ]
}

2. component (Component Instance)

Renders an instance of a reusable component defined in components/*.json.

Properties

Property

Type

Required

Description

type

"component"

Yes

Node type discriminator

component

string

Yes

Name of the component (matches filename without .json)

props

Record<string, any>

No

Prop values passed to the component

children

`(string | Node)[]`

No

Children inserted into the component's slot (if it has one)

attributes

`Record<string, string | number | boolean>`

No

HTML attributes on the wrapper element

style

StyleObject

No

Wrapper style (prefer using props with style mappings instead)

label

string

No

Custom label in the editor structure tree

if

`boolean | object | string`

No

Conditional rendering expression

Do not add style directly to component instances unless necessary. Define props in the component interface that map to styles via _mapping objects.

Examples

Simple component with props:

{
  "type": "component",
  "component": "Button",
  "props": { "text": "Click me", "variant": "primary" }
}

Component with slot children:

{
  "type": "component",
  "component": "Card",
  "props": { "title": "Featured" },
  "children": [
    { "type": "node", "tag": "p", "children": "Card body content goes here." }
  ]
}

3. link

Renders as a <div> in the editor and as an <a> tag in the static build.

Properties

Property

Type

Required

Description

type

"link"

Yes

Node type discriminator

href

`string | LinkMapping`

Yes

URL string or link mapping object

children

`string | (string | Node)[]`

No

Link content

style

StyleObject

No

Responsive style object

attributes

`Record<string, string | number | boolean>`

No

HTML attributes (e.g., target, rel)

label

string

No

Custom label in the editor structure tree

interactiveStyles

InteractiveStyleRule[]

No

CSS rules for pseudo-states

if

`boolean | object | string`

No

Conditional rendering expression

Examples

Static link:

{
  "type": "link",
  "href": "/about",
  "children": ["Learn more"]
}

Link with target attribute:

{
  "type": "link",
  "href": "https://github.com",
  "attributes": { "target": "_blank", "rel": "noopener noreferrer" },
  "children": ["GitHub"]
}

Dynamic href in a component (using a link-type prop):

{
  "type": "link",
  "href": "{{link}}",
  "children": ["{{linkText}}"]
}

The component interface defines the prop as:

{
  "link": { "type": "link", "default": { "href": "/", "target": "_blank" } }
}

Link mapping for prop-based href resolution:

{
  "type": "link",
  "href": { "_mapping": true, "prop": "link" }
}

4. embed

Renders raw HTML or SVG content. Useful for icons, third-party widgets, or custom markup.

Properties

Property

Type

Required

Description

type

"embed"

Yes

Node type discriminator

html

string

Yes

Raw HTML/SVG string

style

StyleObject

No

Responsive style object for the wrapper

attributes

`Record<string, string | number | boolean>`

No

HTML attributes on the wrapper

label

string

No

Custom label in the editor structure tree

if

`boolean | object | string`

No

Conditional rendering expression

Examples

SVG icon:

{
  "type": "embed",
  "html": "<svg viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\"><path d=\"M5 12h14M12 5l7 7-7 7\"/></svg>",
  "style": { "base": { "width": "24px", "height": "24px" } }
}

Third-party embed:

{
  "type": "embed",
  "html": "<iframe src=\"https://www.youtube.com/embed/dQw4w9WgXcQ\" frameborder=\"0\" allowfullscreen></iframe>",
  "style": { "base": { "width": "100%", "aspectRatio": "16/9" } }
}

5. list

Renders children repeatedly for each item from a data source. Supports two source types: component props (prop) and CMS collections (collection).

Properties

Property

Type

Required

Default

Description

type

"list"

Yes

Node type discriminator

sourceType

`"prop" | "collection"`

No

"prop"

Where items come from

source

string

Yes

Prop name or collection name

tag

`string | false`

No

"div"

Container element tag, or false for no wrapper

itemAs

string

No

"item" (prop) or singularized collection name

Variable name for each item in templates

limit

number

No

Maximum items to render

offset

number

No

Number of items to skip

children

Node[]

No

Template repeated for each item

style

StyleObject

No

Style for the container element

label

string

No

Custom label in the editor structure tree

if

`boolean | object | string`

No

Conditional rendering expression

Collection-only properties (ignored when sourceType is "prop"):

Property

Type

Description

items

`string | string[]`

Direct item IDs or template expression for referenced items

filter

`FilterCondition | FilterCondition[] | Record`

Filter conditions

sort

`SortConfig | SortConfig[]`

Sort configuration

excludeCurrentItem

boolean

Exclude the current CMS page item from results

emitTemplate

boolean

Emit a <template> element for client-side dynamic rendering

Examples

List from component prop:

{
  "type": "list",
  "sourceType": "prop",
  "source": "items",
  "children": [
    { "type": "node", "tag": "div", "children": "{{item.label}}" }
  ]
}

List from CMS collection with sorting:

{
  "type": "list",
  "sourceType": "collection",
  "source": "posts",
  "limit": 10,
  "sort": { "field": "createdAt", "order": "desc" },
  "children": [
    {
      "type": "link",
      "href": "{{post._url}}",
      "children": [
        { "type": "node", "tag": "h3", "children": "{{post.title}}" },
        { "type": "node", "tag": "p", "children": "{{post.excerpt}}" }
      ]
    }
  ]
}

When sourceType is "collection", the default item variable is the singularized collection name. For "posts", it becomes {{post.title}}. Override with itemAs:

{
  "type": "list",
  "sourceType": "collection",
  "source": "posts",
  "itemAs": "article",
  "children": [
    { "type": "node", "tag": "h3", "children": "{{article.title}}" }
  ]
}

Filtered collection list:

{
  "type": "list",
  "sourceType": "collection",
  "source": "posts",
  "filter": { "field": "category", "operator": "eq", "value": "tech" },
  "sort": { "field": "_createdAt", "order": "desc" },
  "limit": 5,
  "excludeCurrentItem": true,
  "children": [
    { "type": "node", "tag": "div", "children": "{{post.title}}" }
  ]
}

List item context variables available in children templates:

Variable

Description

{{item.field}} or {{customName.field}}

Field value from the current item

{{itemIndex}} or {{customNameIndex}}

Zero-based index of the current item

{{itemFirst}} or {{customNameFirst}}

true for the first item

{{itemLast}} or {{customNameLast}}

true for the last item


6. locale-list (Language Switcher)

Renders a locale switcher that generates links for each configured language. Requires i18n to be configured in project.config.json.

Properties

Property

Type

Required

Default

Description

type

"locale-list"

Yes

Node type discriminator

displayType

`"code" | "name" | "nativeName"`

No

"nativeName"

How locale text is displayed

showFlag

boolean

No

true

Show flag icons

showCurrent

boolean

No

true

Include the active locale in the list

showSeparator

boolean

No

true

Show separators between items

style

StyleObject

No

Style for the container

itemStyle

StyleObject

No

Style for each locale item

activeItemStyle

StyleObject

No

Style for the active locale item

separatorStyle

StyleObject

No

Style for separators

flagStyle

StyleObject

No

Style for flag icons

label

string

No

Custom label in the editor structure tree

if

`boolean | object | string`

No

Conditional rendering expression

Examples

Full language switcher with native names and flags:

{
  "type": "locale-list",
  "displayType": "nativeName",
  "showFlag": true,
  "showCurrent": true,
  "showSeparator": true,
  "style": { "base": { "display": "flex", "gap": "8px", "alignItems": "center" } },
  "itemStyle": { "base": { "fontSize": "14px", "color": "var(--text-secondary)" } },
  "activeItemStyle": { "base": { "fontWeight": "700", "color": "var(--text-primary)" } }
}

Minimal code-only switcher:

{
  "type": "locale-list",
  "displayType": "code",
  "showFlag": false,
  "showSeparator": false
}

7. slot

Marks where instance children are inserted inside a component structure. Used only in component definitions (components/*.json), not in pages.

Only one slot per component is allowed.

Properties

Property

Type

Required

Description

type

"slot"

Yes

Node type discriminator

default

`(string | Node)[]`

No

Default content when no children are passed

Examples

A component structure with a slot:

{
  "component": {
    "interface": {
      "title": { "type": "string", "default": "Card Title" }
    },
    "structure": {
      "type": "node",
      "tag": "div",
      "style": { "base": { "padding": "16px", "border": "1px solid var(--border)" } },
      "children": [
        { "type": "node", "tag": "h3", "children": "{{title}}" },
        { "type": "slot" }
      ]
    }
  }
}

Slot with default content:

{
  "type": "slot",
  "default": [
    { "type": "node", "tag": "p", "children": "Default slot content" }
  ]
}

Shared Properties

All node types (except slot) support these properties:

Property

Description

if

Conditional rendering. Accepts true/false, a template expression ("{{showBanner}}"), or a style mapping object.

label

Custom display name shown in the editor's structure tree instead of the default.

style

Responsive style object. See Style Properties.

interactiveStyles

CSS pseudo-state rules. See Style Properties.

attributes

HTML attributes passed through to the rendered element.


Next Steps

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

© 2026 Company. All rights reserved.