Menus and fragments

Navigation and the pieces every page shares, saved per language.

4 min read

What they are

A menu is a tree of links, such as the main navigation or the footer links. A fragment is a block tree that many pages share, such as the header, the footer or a top banner. Both are saved per language, so header in id and header in en are two records.

Both are site wide. A change is not tied to one page and not to one revision, so it does not have a draft step. Saving a menu or a fragment changes live output at once. That is why writing needs a publisher (or an admin). Anyone who belongs to the brand can read them.

Menus

GET/v1/manage/menus

Lists every menu of the brand, ordered by key and language.

PUT/v1/manage/menus/{key}/{locale}

Replaces the whole tree of one menu, or creates it. locale must be a language of the brand, else the answer is 404. The key is a free name, and a theme can declare the keys it renders in menuLocations.

requestjson
{
  "items": [
    {
      "label": "Products",
      "url": "/products",
      "eyebrow": "Catalogue",
      "key": "products",
      "children": [
        {
          "label": "Spare parts",
          "description": "Screens, batteries and chargers",
          "children": [
            { "label": "Screens", "url": "/products/screens" }
          ]
        }
      ]
    },
    { "label": "Contact", "url": "/contact" }
  ]
}

Item fields

Every item, at every depth, accepts these fields. Any other key is dropped.

FieldRule
labelRequired, plain text, up to 255 characters.
urlOptional, up to 2048 characters, must be a safe link.
childrenOptional list of items, up to 100.
eyebrowPlain text, up to 120.
descriptionPlain text, up to 500.
media_idId of a ready media file of this brand. Another brand's id or a missing file is a 422.
attributesUp to 10 pairs of label (60) and value (120), plain text.
keyA stable id for styling or tracking: a-z, 0-9 and -, up to 64.

Plain text means no HTML. A tree holds 500 items at most, children included.

Depth and unsafe links

A menu nests three levels deep at most (top item, tab, tier). If the active theme declares a smaller maxDepth for that menu key in menuLocations, the smaller number applies. A deeper tree is a 422 on items.

A link must be a web address, an email or phone link, or a path on the site. Anything else, such as a javascript: address, is refused and the error names the exact item so the editor can show it on the field:

response (422)json
{ "errors": { "items.0.children.1.url": ["Must be a web address, an email or phone link, or a path on this site."] } }

Reading a menu on your site

GET/v1/public/menus/{key}
terminalbash
curl -s "https://api.atlascms.id/v1/public/menus/header?locale=en" \
  -H "Authorization: Bearer $ATLAS_TOKEN"

If the menu does not exist in the requested language, the API returns the default language one. If neither exists, it answers 404. An item with a media_id comes back with image: { url, alt, width, height }, with alt text in the menu language and then the default language. If the file is gone, image is null.

DELETE /v1/manage/menus/{menu} removes a menu (publisher). A media file used by a menu cannot be deleted until the menu stops using it.

Fragments

GET/v1/manage/fragments
PUT/v1/manage/fragments/{key}/{locale}
requestjson
{
  "blocks": [
    { "id": "h1", "type": "acme-header", "v": 1, "props": {} }
  ],
  "published": true
}

Common keys are header, footer and banner. blocks is a block tree checked like any other (three levels, safe links, props against the block definitions). A fragment never holds the atlas-post-content block, because it appears on many pages and has no single entry body. published: false saves the blocks without making them live. A part with no live override falls back to the default the theme package ships.

Your site does not call a fragments endpoint. The page payload from GET /v1/public/page carries fragments, keyed by area (header, footer, banner). If a language has no fragment, the default language fills the gap.

DELETE /v1/manage/fragments/{fragment} removes one (publisher).

Fragments are template parts

A fragment is an alias of a template part of the active theme. The endpoints above write the same override a part has, and keep the old fragment record as a mirror, so a site that reads fragments keeps working. The alias stays for two minor versions. New work should use the part endpoints, which also give you a draft before publishing:

CallRole
PUT /v1/manage/theme/templates/part/{slug}/draftPublisher, saves { locale?, blocks } as a draft
POST /v1/manage/theme/templates/part/{slug}/publishPublisher, makes the draft live

The theme side of this is explained in Theme development.

How a change reaches the live site

Every save of a menu or a fragment runs the same steps:

  1. The change is saved and an audit row is written (menu.updated, menu.deleted, fragment.updated, fragment.deleted).
  2. The brand content version goes up. The public API builds its ETags from that version, so clients and caches see a new answer at once.
  3. A signed purge webhook is queued to each active webhook of the brand. Its paths is ["*"], because a menu or a fragment can appear on every page.

Your site checks the signature, drops its cached pages, and the next visitor gets the new header. See Webhooks and purge for the headers and a verification example.

No draft for menus

A menu has no draft or schedule. If you need to prepare a new navigation first, build it under another key, then switch your site to read that key.

A save replaces the tree

PUT sends the whole menu. Read it with GET /v1/manage/menus first, change the items you need, then send the full tree back.

Was this page helpful?

Suggest a change