Content Types
Contents
The content type is the foundational schema type in Enonic XP. All content items must be based on a content type, which defines the structure and capabilities of the content.
Identifier
Every content type has a unique identifier in the following format <namespace>:<name>. Content types are either built-in, or custom defined by developers. As an example, a content type identifier might be myapp:article.
Usage
Content types schemas are defined via YAML definitions. Below is an example content type definition:
kind: "ContentType" (1)
title: "Person" (2)
displayNamePlaceholder: "Full Name" (3)
description: "Create a new person" (4)
superType: "base:structured" (5)
abstract: false (6)
final: true (7)
allowChildContent: true (8)
allowChildContentType: (9)
- "base:folder"
- "${app}:article-*"
form: (10)
- type: "ContentSelector"
name: "spouse"
label: "Spouse"
occurrences:
min: 0
max: 1
allowContentType: (11)
- "${app}:person"
| 1 | kind identifies this as a ContentType schema. |
| 2 | title (required) The human readable name of the content type. Optionally use localization to define a mapping to localize the value. The localization key must then be declared and localised in the resource bundle. |
| 3 | displayNamePlaceholder (optional) Sets a different label for the built-in display name field — editors here see <Full Name> instead of the generic <Display Name>. The person’s name is the display name, so it needs no form item of its own. |
| 4 | description (optional) Set a description that is shown when creating the content type. |
| 5 | superType (required) Refers to one of the base types. You should normally use base:structured. |
| 6 | abstract (optional; default: false) If true, you cannot create content with this content type. |
| 7 | final (optional; default: false) If true, it is not possible to create new content types that "extend" this. |
| 8 | allowChildContent (optional; default: true) If false, no content will be allowed to be created or moved under content of this content type (e.g. prevents child content under media) |
| 9 | allowChildContentType (optional) If specified, only content of content types matching specified criteria will be allowed to be created or moved under content of this content type. The pattern matching used is the same as the one described in MATCH. |
| 10 | form (optional) The Form definition for your content type — a list of form items. |
| 11 | Input-specific settings, like allowContentType here, are placed directly on the input — there is no config: wrapper in YAML. See each form item for its available settings. |
allowChildContentType has no effect if allowChildContent is set to false |
Display name
Every content type has a built-in display name field, the value isstored in the top-level displayName property. It is the item’s human-readable title. The display name is shown in Content Studio lists and content selectors, and returned as displayName by the APIs for every content type.
Because it is built in, it is never declared in the form:. Adding a title, heading or name input of your own stores the same information twice — editors get two fields to fill in, and front-end code gets two places to look.
kind: "ContentType"
title: "Article"
superType: "base:structured"
form:
- type: "TextLine"
name: "title"
label: "Title"
kind: "ContentType"
title: "Article"
superType: "base:structured"
displayNamePlaceholder: "Article heading"
form: []
Model a separate field only when it carries genuinely different information from the item’s name — a short listing teaser alongside a longer full title, for example.
Three optional top-level settings adapt the display name to a content type, and none of them adds a form item:
-
displayNamePlaceholder -
Sets a different label for the display name field in the content form — editors see
<Article heading>instead of the generic<Display Name>. Supports localization via thetext/i18nobject pattern. -
displayNameExpression -
Composes and persists the
displayNamevalue from other form fields, for items whose name is derived rather than typed. -
displayNameListExpression -
Changes only how items are labelled in Content Studio’s list views, leaving the stored value untouched.
Composing the display name is the client’s job, not the platform’s — XP stores whatever value it is given. Content Studio is responsible for calculating the displayNameExpression, and for deriving the content item’s URL name (_name) from the display name as the editor types. Content created through the APIs or by import must supply its own displayName. |
displayNameExpression
Composes the display name automatically while the content is being edited, instead of the editor typing it manually:
kind: "ContentType"
title: "Person"
superType: "base:structured"
displayNameExpression: "${first_name} ${last_name}"
form:
- type: "TextLine"
name: "first_name"
label: "First name"
- type: "TextLine"
name: "last_name"
label: "Last name"
Placeholders on the form ${field_name} reference form inputs by name — use dotted paths for inputs nested inside sets, e.g. ${address.city}. Empty fields resolve to nothing (surplus whitespace is collapsed), HTML tags are stripped from values, and HtmlArea inputs are ignored.
The composed value is saved as the content’s displayName, so it is returned by the APIs like any other display name. With the definition above, a person with first name Django and last name Reinhardt gets the display name Django Reinhardt.
displayNameListExpression
Overrides how items of this type are labelled in Content Studio list views, such as the content navigator and selector dropdowns — typically by combining the displayName with other form values:
kind: "ContentType"
title: "Person"
superType: "base:structured"
displayNameListExpression: "${displayName} (${data.email})"
form:
- type: "TextLine"
name: "email"
label: "Email"
Placeholders may reference ${displayName} and any stored form value via ${data.field_name}. Missing or empty properties resolve to an empty string.
This setting only affects presentation in the Content Studio UI — the stored displayName is not modified, and API responses are unaffected. With the definition above, a person named Django Reinhardt with email django@example.com is listed in Content Studio as Django Reinhardt (django@example.com).
Querying via GraphQL
Guillotine generates a dedicated GraphQL type for every content type in the project. Each typed field mirrors the YAML form definition, so a front-end developer gets a schema that matches the content shape without any manual mapping.
Type names
Guillotine sanitizes the content type name for GraphQL by replacing the namespace separator and any dashes with underscores:
| Content type | GraphQL type |
|---|---|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
Untyped access
Every content item implements the Content interface, which exposes standard fields available without inline fragments. Use dataAsJson to get the form data as raw JSON — useful when the client does not care about the specific content type:
{
guillotine {
get(key: "/artists/django-reinhardt") {
_id
_path
displayName
type
dataAsJson
}
}
}
{
"data": {
"guillotine": {
"get": {
"_id": "f3076b5c-ea45-4c8b-8c06-1f87b8d8cdd9",
"_path": "/artists/django-reinhardt",
"displayName": "Django Reinhardt",
"type": "com.example.myproject:artist",
"dataAsJson": {
"name": "Django Reinhardt",
"about": "Belgian-Romani jazz guitarist."
}
}
}
}
}
Typed access
To access form fields as typed GraphQL fields, use an inline fragment on the generated type:
{
guillotine {
get(key: "/artists/django-reinhardt") {
displayName
... on com_example_myproject_Artist {
data {
name
about
}
}
}
}
}
{
"data": {
"guillotine": {
"get": {
"displayName": "Django Reinhardt",
"data": {
"name": "Django Reinhardt",
"about": "Belgian-Romani jazz guitarist."
}
}
}
}
}
Querying by content type
Use queryDsl with a term query on the type field to fetch all content of a given type. Combine with an inline fragment to return typed data:
{
guillotine {
queryDsl(
query: { term: { field: "type", value: { string: "com.example.myproject:artist" } } },
first: 10,
sort: { field: "displayName", direction: ASC }
) {
_id
displayName
... on com_example_myproject_Artist {
data {
name
}
}
}
}
}
See the Guillotine documentation for the full list of queries, arguments, and DSL options.
Icon
A content type may optionally have its own specific icon which is used to represent the content type in Content Studio. The icon can be assigned to the content type by supplying a PNG or SVG file with the same name as the content type i.e. my-content-type.svg.
Abstract base type
Every content type extends base:structured, the abstract parent for all schema-driven content. It introduces the form mechanism and is the common foundation for custom content types.
- superType
-
base:content
- abstract
-
true
- final
-
false
- allowChildContent
-
true
| You may not specify a custom content type as a supertype, nor may you create circular dependencies between content types. |
Media content has its own abstract parent (base:media) with a different set of capabilities — see Media content types.
Built-in types
Enonic XP ships with a set of concrete, ready-to-use content types:
-
base:folder, base:shortcut, base:unstructured — generic structural types; see Built-in content types.
-
media:*— concrete media types created automatically from uploaded files; see Media content types. -
portal:site,portal:page-template,portal:fragment,portal:template-folder— types supporting the pages concept; see Sites and pages.
Extend with mixins
You may dynamically extend both built-in and custom-defined content types through the use of Mixins.