Page components

Contents

At least one page component must be available in your project to be able to create pages.

Introduction

At the root of every page, you will find the page component. Most sites only need a single page component, as this can be reused across all pages.

To create a page component, place a descriptor file in your project: src/main/resources/site/pages/<page-name>/<page-name>.yaml.

The name you choose for a component will be used in the underlying data of the stored page. So choose your name wisely.

Usage

Sample page descriptor
kind: "Page"
title:  (1)
  text: "My first page"
  i18n: "component.page.name"
description: "Front page of our site"  (2)
form: []  (3)
regions:  (4)
  - name: "main"
1 title provides a display name used by the editorial interface. Supports localization via the text/i18n object pattern.
2 description Description field shown when creating a part in Content Studio
3 form allows the definition of a configuration form based on the schema system
4 regions optionally specify one or more regions for the page
A page component may define zero to many regions. Conventionally, a page should at least define a single region called main.

Regions

Regions are explicitly named drop zones where editors can place other page components. Only page and layout components support regions. Each region must have a unique name within the component.

From Content Studio, this is what regions may look like in the page form:

Component editor

And here is what the same structure might look like in the visual page editor.

The dropzones are empty regions.
Layout with three empty regions

Output

Using the Content API, you may access a JSON representation of the hierarchical structure of components and regions on a page.

Sample JSON from the Content API
{
  "page": {
    "type": "page",
    "path": "/",
    "descriptor": "com.enonic.app.superhero:default",
      "components": [
        {
          "path": "/main/0",
          "type": "layout",
          "descriptor": "com.enonic.app.superhero:three-column",
          "config": {},
          "regions": {}
        }
      ]
  }
}
Empty regions are never persisted. Region names are only determined from the component path.

Querying via GraphQL

Guillotine exposes pages in two shapes — a tree rooted at the pageAsJson field, or the strongly typed flat list under components (see Component indexing).

PageAsJson

This query returns the hierarchical page structure as JSON, with all components and regions nested under the pageAsJson field. Useful for rendering the page as-is, but does not provide typed access to component config fields.

{
  guillotine {
    get(key: "/path/to/page") {
      displayName
      pageAsJson
    }
  }
}
Response
{
  "data": {
    "guillotine": {
      "get": {
        "displayName": "Hello World",
        "pageAsJson": {
          "type": "page",
          "path": "/",
          "descriptor": "com.enonic.app.superhero:default",
          "components": [
            {
              "path": "/main/0",
              "type": "layout",
              "descriptor": "com.enonic.app.superhero:three-column",
              "config": {},
              "regions": {}
            }
          ]
        }
      }
    }
  }
}

Component list

To access the full list of components on a page, regardless of their position in the tree, use the components field on the page query. This allows you to perform queries against typed component data, similar to how you would with any other content type.

This structure mirrors how components are stored and indexed, giving a 1:1 mapping between query shape and indexed shape.

{
  guillotine {
    get(key: "/site/articles/hello") {
      components(resolveFragment: true) {
        type
        path
        page {
          descriptor
          configAsJson
        }
        layout {
          descriptor
          configAsJson
        }
        part {
          descriptor
          configAsJson
        }
      }
    }
  }
}
Response
{
  "data": {
    "guillotine": {
      "get": {
        "components": [
          {
            "type": "page",
            "path": "/",
            "page": {
              "descriptor": "com.enonic.app.superhero:default",
              "configAsJson": null
            },
            "layout": null,
            "part": null
          },
          {
            "type": "layout",
            "path": "/main/0",
            "page": null,
            "layout": {
              "descriptor": "com.enonic.app.superhero:two-column",
              "configAsJson": null
            },
            "part": null
          },
          {
            "type": "part",
            "path": "/main/0/left/0",
            "page": null,
            "layout": null,
            "part": {
              "descriptor": "com.enonic.app.superhero:featured",
              "configAsJson": {
                "headline": "Welcome",
                "showLogo": true
              }
            }
          }
        ]
      }
    }
  }
}

See Fragments for the trade-offs between server-side resolution and manual traversal, Parts for typed access to a part’s form config, and Component indexing for searching across pages by component.


Contents

Contents