Navigation¶

The navigation surface bundles the REST services a frontend uses to render global navigation, contextual sidebars, the navigation root, the site footer and language switching. All of these are customizations on top of the standard Plone RestAPI endpoints.

This page goes deeper on the dedicated navigation services. For the high-level overview of @navigation (including SubTopic handling) and @navroot, see the Restapi page – this page does not repeat that material but documents what each endpoint returns and how a frontend consumes it.

Overview¶

Endpoint

Purpose

@navigation

Flat top-level navigation tree for the global navbar

@navroot

The navigation root of the current context (site or subsite)

@contextnavigation

Contextual (sidebar) navigation relative to the current page

@footer

Footer fields configured on the navigation root

@translations

Available language translations for the current content

All endpoints are available both as standalone GET requests and as expandable components. When a request is made with ?expand=<name>, the data is embedded under @components in the content response; otherwise only the @id of the component is returned so the frontend can fetch it separately.

@navigation¶

Returns the global navigation tree starting at the navigation root. This is the data source for the main navbar. The 7inOne implementation adds caching and lets SubTopic content appear under every one of its main topics rather than only at its physical location.

GET /Plone/topics/@navigation HTTP/1.1
Host: localhost:8080
Accept: application/json

Response:

HTTP/1.1 200 OK
Content-Type: application/json

{
    "@id": "http://localhost:8080/plone/topics/@navigation",
    "items": [
        {
            "@id": "http://localhost:8080/plone/topics/main-topic-1",
            "title": "Main Topic 1",
            "type": "Topic",
            "items": [
                {
                    "@id": "http://localhost:8080/plone/topics/subtopic-a",
                    "title": "SubTopic A",
                    "type": "SubTopic"
                }
            ]
        },
        {
            "@id": "http://localhost:8080/plone/topics/main-topic-2",
            "title": "Main Topic 2",
            "type": "Topic",
            "items": [
                {
                    "@id": "http://localhost:8080/plone/topics/subtopic-a",
                    "title": "SubTopic A",
                    "type": "SubTopic"
                }
            ]
        }
    ]
}

Each item carries an @id, a title, a type (the portal type) and a nested items array for children. A SubTopic is repeated under each of its main topics, so the same item @id can appear in more than one branch of the tree.

The SubTopic mechanics are covered in more detail on the Restapi page.

@navroot¶

Returns the navigation root for the current context. On a plain site this is the Plone site root; inside a Subsite it is the subsite itself. The frontend uses this to know which root the current page belongs to – for example to scope navigation, theming or search to a subsite.

GET /Plone/subfolder/@navroot HTTP/1.1
Host: localhost:8080
Accept: application/json

Response:

HTTP/1.1 200 OK
Content-Type: application/json

{
    "@id": "http://localhost:8080/plone/subfolder/@navroot",
    "navroot": {
        "@id": "http://localhost:8080/plone",
        "@type": "Plone Site",
        "title": "My Site",
        "description": "Welcome to my Plone site",
        "is_folderish": true,
        "review_state": "published"
    }
}

The navroot object is the summary serialization of the root content (@id, @type, title, description, review_state). Child items are intentionally omitted – the frontend fetches the tree itself through @navigation or @contextnavigation.

@contextnavigation¶

Returns navigation relative to the current context, the data source for a contextual sidebar or section menu. Unlike @navigation (which always starts at the root), this tree is built around the current page.

The 7inOne implementation defaults the bottomLevel to 1 when the caller does not pass one, and enriches every node with a thumbnail, an icon and a cleaned href.

GET /Plone/topics/my-page/@contextnavigation HTTP/1.1
Host: localhost:8080
Accept: application/json

Response structure:

{
    "@id": "http://localhost:8080/Plone/topics/my-page/@contextnavigation",
    "items": [
        {
            "@id": "http://localhost:8080/Plone/topics/my-page/child",
            "title": "Child Page",
            "description": "",
            "href": "http://localhost:8080/Plone/topics/my-page/child",
            "type": "document",
            "icon": "",
            "thumb": "",
            "is_current": false,
            "is_in_path": true,
            "is_folderish": true,
            "normalized_id": "child",
            "review_state": "published",
            "items": []
        }
    ]
}

Fields a frontend typically consumes per node:

href The link target. Internal resolveuid links are resolved to absolute URLs, and direct @@download links are stripped back to the object URL so the navigation never links straight into a download.

title The navigation label. A nav_title override on the content takes precedence over the regular title.

type The normalized portal type, usable as a CSS hint.

icon For File items, the URL of the matching MIME-type icon. Empty for other types or when icons are disabled.

thumb A scaled image URL (.../@@images/image/<scale>) when the item has an image and thumbnails are enabled. Empty otherwise.

is_current true when the node is the current context.

is_in_path true when the node is an ancestor of the current context. Use this to expand the branch leading to the active page.

is_folderish true when the node can contain children.

items Child nodes, populated up to bottomLevel. With the default bottomLevel=1 only the immediate children of the starting node are expanded.

Controlling depth¶

Pass an explicit bottom level to expand more levels. A value of 0 recurses without a depth limit.

GET /Plone/topics/my-page/@contextnavigation?expand.contextnavigation.bottomLevel=0 HTTP/1.1
Host: localhost:8080
Accept: application/json

@footer¶

Returns the footer fields configured on the navigation root. Footer fields are ordinary schema fields on the root content whose names start with footer_. The endpoint strips the footer_ prefix and returns the remaining fields as a flat object, so adding a footer field is a pure schema change – no code change is required.

The endpoint is available on any navigation root, which means each Subsite can carry its own footer.

GET /Plone/@footer HTTP/1.1
Host: localhost:8080
Accept: application/json

Response structure (for a root with a single footer_textline field):

{
    "@id": "http://localhost:8080/Plone/@footer",
    "textline": "Some text for the footer"
}

The keys correspond to the field names with the footer_ prefix removed (footer_textline becomes textline). Each value is the serialized field value, so richer field types (rich text, images, JSON link lists) serialize to their usual REST representation. Field values are only included when the requesting user has read permission for that field.

@translations¶

Returns the available translations for the current content plus the translations of the navigation root. This is the data source for a language switcher. The 7inOne implementation always returns an entry for every supported language, falling back to the closest available destination when a direct translation is missing – so the switcher can offer all languages without dead links.

This endpoint is only available when plone.app.multilingual is installed.

GET /Plone/en/my-page/@translations HTTP/1.1
Host: localhost:8080
Accept: application/json

Response structure:

{
    "@id": "http://localhost:8080/Plone/en/my-page/@translations",
    "items": [
        {
            "@id": "http://localhost:8080/Plone/en/my-page",
            "language": "en",
            "current": true,
            "name": "English",
            "native": "English"
        },
        {
            "@id": "http://localhost:8080/Plone/de/meine-seite",
            "language": "de",
            "current": false,
            "name": "German",
            "native": "Deutsch"
        }
    ],
    "root": {
        "en": "http://localhost:8080/Plone/en",
        "de": "http://localhost:8080/Plone/de"
    }
}

items One entry per supported language. Each entry has the target @id to switch to, the language code, a current flag marking the active language, and the human-readable name and native language names. When a language has no direct translation, @id points at the closest available destination.

root A mapping of language code to the URL of the navigation root in that language. Use it to switch the whole site/subsite to another language rather than a single page. The root key is omitted when the context is not inside a language root folder.

Integration Guide¶

Rendering the global navbar¶

JavaScript:

async function fetchNavigation(rootUrl) {
    const response = await fetch(`${rootUrl}/@navigation`, {
        headers: { 'Accept': 'application/json' }
    });
    const data = await response.json();
    return data.items;
}

const items = await fetchNavigation('http://localhost:8080/Plone');
items.forEach(item => renderNavEntry(item.title, item['@id'], item.items));

Rendering a contextual sidebar¶

JavaScript:

async function fetchContextNavigation(pageUrl, bottomLevel = 1) {
    const url = `${pageUrl}/@contextnavigation` +
        `?expand.contextnavigation.bottomLevel=${bottomLevel}`;
    const response = await fetch(url, {
        headers: { 'Accept': 'application/json' }
    });
    const data = await response.json();
    return data.items;
}

const items = await fetchContextNavigation('http://localhost:8080/Plone/topics/my-page');
items.forEach(node => {
    renderEntry(node.title, node.href, {
        active: node.is_current,
        open: node.is_in_path,
        children: node.items,
    });
});

Fetching components in one request¶

Expand several navigation components together with the page content:

GET /Plone/topics/my-page?expand=navigation,navroot,contextnavigation,translations HTTP/1.1
Host: localhost:8080
Accept: application/json

Each expanded component appears under @components in the response, keyed by its name (navigation, navroot, contextnavigation, translations). Without expand, @components contains only the @id of each component so the frontend can decide which ones to fetch.

Python:

import requests

response = requests.get(
    'http://localhost:8080/Plone/topics/my-page',
    params={'expand': 'navigation,navroot,translations'},
    headers={'Accept': 'application/json'},
)
components = response.json()['@components']
navigation = components['navigation']['items']
translations = components['translations']['items']

7inOne

Navigation

Contents

  • Technical Documentation
    • Background Jobs
    • Banner
    • Book and Library
    • Caching & Cloudflare Invalidation
    • Call to Action
    • Site Distributions
    • Document Date
    • Empty Trash
    • Exclude Items by Default
    • External Data Fetchers
    • ImageReferenceField
    • JSON Schema Widget
    • Keycloak Integration
    • Matomo Stats
    • Multilingual
    • Navigation
      • Overview
      • @navigation
      • @navroot
      • @contextnavigation
      • @footer
      • @translations
      • Integration Guide
    • Notifications
    • OIDC Integration
    • Opening Hours
    • Public PDF API
    • RAG (Retrieval-Augmented Generation)
    • Responsible Unit
    • REST API 404 for Inactive Content
    • Restapi
    • Elasticsearch Integration (RESTAPI)
    • SAML Integration
    • Elasticsearch Search
    • Staging (Working Copies)
    • Subsite
    • Task System
    • Topics and SubTopics
    • Trash (Soft Delete & Restore)
    • Versioning
    • Website Workflow

Related Topics

  • Documentation overview
    • Technical Documentation
      • Previous: Multilingual
      • Next: Notifications
©2025, maethu. | Powered by Sphinx 8.0.2 & Alabaster 1.0.0 | Page source