Rich text: HtmlArea
Contents
If you want to work with rich text in Content Studio, HtmlArea is your friend. This rich text input type offers some very powerful editing capabilities and a lot of configuration options. This whole chapter is dedicated to the HtmlArea: how it works and how to use it in a headless fashion.
The tasks and examples in this chapter are based on work done in previous chapters (starting with the sandboxes chapter). If you want to follow along with the examples, make sure you’re all caught up. |
Overview

The HtmlArea input type gives you a multi-line input field with rich formatting options, much like what you’re used to from your favorite word processor. It supports text formatting and justification, lists, links, and even lets you insert images.
You can configure the HtmlArea to be exactly what you want by dictating what formatting tools to include or exclude (the include
and exclude
attributes, respectively), by what heading levels are allowed (allowedHeadings
), and by giving it a default value (default
).
Additionally, the HtmlArea supports the use of macros, giving you the ability to insert prefabricated rich components into your markup.
Configuration of text tools
We can use the HtmlArea’s config
section to specify which rich text controls should be available when editing. Using a combination of the include
and exclude
tags, we can customize the input’s appearance.
By default, it includes a limited, but useful selection of tools at your disposal. Using <include>*</include>
as an option is the same as including the default tools.
<input type="HtmlArea" name="everything">
<label>HtmlArea</label>
</input>
If you want to exclude all controls, use <exclude>*</exclude>
:
<input type="HtmlArea" name="nothing">
<label>HtmlArea with exclude: *</label>
<config>
<exclude>*</exclude>
</config>
</input>

<exclude>*</exclude>
and a selection of tools.
Finally, if you want to exclude most tools, but not all, you can exclude all and then add back in the ones you want:
<input type="HtmlArea" name="something">
<label>HtmlArea with some controls</label>
<config>
<exclude>*</exclude>
<include>Format | Bold Italic Underline | JustifyBlock
JustifyLeft JustifyCenter JustifyRight | HorizontalRule Blockquote</include>
</config>
</input>

<exclude>*</exclude>
For a full overview of the available text tools and HtmlArea configuration options, visit the HtmlArea input type reference docs. Additionally, you can find a more thorough explanation of the toolbar and its tools in the rich text editor reference documentation.
Task: Animal description
Let’s add a rich-text description field to our animals. The input should be optional and have at most a single occurrence. You can include any editor tools you want, but it must at least have the Link
,Image
and Macro
tools, as we’ll be using these later. Otherwise, configure it as you want.
Your content type should now look something like this:
<?xml version="1.0" encoding="utf-8"?>
<content-type>
<display-name>Animal</display-name>
<description>An animal that lives on planet Earth</description>
<super-type>base:structured</super-type>
<form>
<input type="TextLine" name="otherNames">
<label>Other names</label>
<help-text>Other names for this species.</help-text>
<occurrences minimum="0" maximum="0" />
</input>
<input name="images" type="ImageSelector">
<label>Images</label>
<help-text>Images of the animal</help-text>
<occurrences minimum="0" maximum="3" />
</input>
<input name="description" type="HtmlArea">
<label>Description</label>
<help-text>
Describe the animal. Where its habitats are, what it eats, etc.
</help-text>
<config>
<exclude>*</exclude>
<include>Format Link Macro Image</include>
</config>
</input>
</form>
</content-type>
With the new content type deployed, you are ready to complete the specific tasks below.
Links
In addition to good old URLs, in the HtmlArea, you may also insert links to other content items, links to download media files, or links to e-mail.
An important feature, is that when creating internal links (content or media links), Enonic will save a references to the target items, similar to how the ContentSelector works. This is useful when looking for usage and dependencies between content items later.
Because links are created using the item’s unique id. It is perfectly safe to move items around in the tree structure, or change it’s path name. The links will not break. |
Task: Internal link
In this task, you’ll create an internal link to another animal.
Create a new animal, "Lynx".
-
In the
Description
field, type some text, i.e. "Lynx are cats, just like lions". Select the "lions" text and click "insert link" in the toolbar.You can also use ctrl+K to insert the link. -
You’ll be greeted by the "Insert link" dialog: .Choose the content tab, and select the previously created "Lion" as the target item
When done, save the item - but keep the tab open to complete the upcoming tasks
Images
With the image tool enabled, you insert an image anywhere within your document. During the insert process, you may customize the image further.
The following options are available: Caption, alt text, change the image’s justification, and apply a predefined image styles.
Task: Animal pictures
Try out the image controls that Content Studio give you by uploading a picture and seeing what you can do with it.
We looked at editing images in the previous chapter, but when inserting images in the HtmlArea, you also have something called "image styles", which we’ll have a look at now.
Continue editing the "Lynx" content created in the previous task.
-
Insert an image below the existing text. Use the "insert image" button in the toolbar.
Figure 4. The toolbar with the image tool highlightedYou can also use ctrl+L to insert an image. -
You’ll be greeted by the "insert image" dialog:
Figure 5. The HtmlArea insert image dialog. Photo by Kurt Cotoaga 🦁 -
When accessing the "image styles" dropdown, you’ll find that it’s only got two options and neither of them seem to do much. To make this more exciting, let’s also create a custom image style.
In your app, add a new file:
src/main/resources/site/styles.xml<styles> <image name="square-grayscale"> <display-name>Grayscale</display-name> <aspect-ratio>1:1</aspect-ratio> (1) <filter>hsbcolorize(0x000000); grayscale(); border(6, 0x000000)</filter> (2) </image> </styles>
1 The aspect ratio controls how the image is displayed. 1:1 makes the image square. 2 The filters add a grayscale effect and then a border The styles reference doc has more information on how you can style your images, so go check it out for a deeper dive. -
With this style picked up by XP, you’ll have the "Grayscale" option available in the image styles dropdown. Select it to apply it to the image.
Figure 6. Example image with an the style applied
Macros
Macros enable you to insert custom components within your rich text field. When the content is rendered, the macro is replaced with the actual component implementation.
Similar to content types, macros can define forms that help editors configure them.
Task: Panel Macros
The Panel Macros app comes with a set of pre-defined macros that may look something like this when rendered:

To use these macros, we first need to install the Panel Macros.
-
Navigate to the Applications page (via the XP menu).
-
Press "Install"
-
Search for and install the "Panel Macros" app.
-
Edit your site (My First Site). In the applications dropdown, find Panel Macros, and add it.
-
Save and exit.
Next up, let’s add a macro to the "Lynx" content item.

-
Click "insert macro".
-
Select one of the panels. If you have no preference, try the "Info panel".
-
Add a header and some text to it. For instance:
Lynx macro content- Header
-
Habitats
- Text
-
The lynx inhabits high altitude forests with dense cover of shrubs, reeds, and tall grass.
Figure 9. The insert macro form with habitat data
-
If configured, you may also get a preview of what the macro will look like.
Figure 10. The macro preview function
The Macro can be previewed because the Panel Macros includes a standard preview. |
For more information on macros, including how to create your very own, visit to the macro reference docs.
Headless
When fetching rich text via the Guillotine API, the most commonly used output is "processed html". This is essentially unstyled, semantic HTML. It’s then up to you to style the content in your client.
Links, images and macros will potentially require additional client-side processing.
The following query returns the "Lynx" content, and the description
field al processed HTML.
{
guillotine {
query(query: "displayName = 'Lynx'")
{
... on com_example_myproject_Animal {
data {
description {
processedHtml
}
}
}
}
}
}
{
"data": {
"guillotine": {
"query": [
{
"data": {
"description": {
"processedHtml": "<p>Lynx are cats, just like <a href=\"/admin/site/preview/default/draft/my-first-site/animals/lion\" data-link-ref=\"b5ccb5ff-7458-4dae-8a4c-bc3f5df9fc63\" title=\"The big cat\">lions</a>.</p>\n\n<figure class=\"captioned conteditor-style-grayscale editor-align-justify\"><img alt=\"A yawning lion cub\" src=\"/admin/site/preview/default/draft/_/image/450a1e29-891b-4d5e-977c-719b5a2b9782:ad62be20ff5129561409bcf5b8aae435e980d09a/width-768/lion-cub.jpg\" data-image-ref=\"3c4d6ce4-4536-4d96-a65d-3e0b81f44f27\" style=\"width:100%\" />\n<figcaption>So sleepy 🤗</figcaption>\n</figure>\n\n<p><editor-macro data-macro-name=\"info\" data-macro-ref=\"1c8cbb83-f0e5-45ce-8027-ced88d165a2d\">The lynx inhabits high altitude forests with dense cover of shrubs, reeds, and tall grass.</editor-macro></p>\n"
}
}
}
]
}
}
}
By unescaping the HTML, you will find the following:
<p>
Lynx are cats, just like <a href="/admin/site/preview/default/draft/my-first-site/animals/lion" data-link-ref="b5ccb5ff-7458-4dae-8a4c-bc3f5df9fc63" title="The big cat">lions</a>.
</p>
<figure class="captioned square-grayscale editor-align-justify">
<img
alt="A yawning lion cub"
src="/admin/site/preview/default/draft/_/image/450a1e29-891b-4d5e-977c-719b5a2b9782:ad62be20ff5129561409bcf5b8aae435e980d09a/block-768-768/lion-cub.jpg?filter=hsbcolorize%280x000000%29%3B+grayscale%28%29%3B+border%286%2C+0x000000%29"
data-image-ref="3c4d6ce4-4536-4d96-a65d-3e0b81f44f27"
style="width:100%" />
<figcaption>So sleepy 🤗</figcaption>
</figure>
<p>
<editor-macro
data-macro-name="info"
data-macro-ref="1c8cbb83-f0e5-45ce-8027-ced88d165a2d">The lynx inhabits high altitude forests with dense cover of shrubs, reeds, and tall grass.
</editor-macro>
</p>
With the excepton of macro’s this is all valid HTML. Below, you’ll learn more about handling these elements.
Links
Looking specifically at the processed html link output above, the most interesting part is the data-link-ref
attribute.
Wondering why the href points to a location within the admin? No worries. Guillotine automatically generates contextual links. When deploying the API to production, you may customize the base url as see fit. |
This example is an internal link, you may want access more details about it, for instance to override the URL, optimize accessibility etc. To acccess these details, you need to extend your query with the links field:
{
guillotine {
query(query: "displayName = 'Lynx'")
{
... on com_example_myproject_Animal {
data {
description {
processedHtml
links {
ref
uri
content {
displayName
_path
type
}
}
}
}
}
}
}
}
By executing this query, you will get some more data. Specifically:
...
"links": [
{
"ref": "6931439f-6c4a-4f66-9838-cbd7a0726e80",
"uri": "content://90685b2f-041d-4114-8828-cf3b68501d3c",
"content": {
"displayName": "Lion",
"_path": "/my-first-site/animals/lion",
"type": "com.example.myproject:animal"
}
}]
...
As you can see, you now have access to the full name of the referenced item, as well as it’s type. Use the ref values from the HTML link element to match it with the specific link data - as you may have more than one link.
Cool, right?
Image srcset
In the processed HTML, you can see the <img>
element with style attributes. Additionally, by looking more closely at the src
value, you will find that the style settings are applied in the image service URL.
..../_/image/<id>:<version>/block-768-768/lion-cub.jpg?filter=hsbcolorize%280x000000%29%3B+grayscale%28%29%3B+border%286%2C+0x000000%29
But wait, there’s more! If you want to optimize how fast images load on various devices, Guillotine can also return the image in multiple different sizes (using the srcset
attribute (MDN)). This is achieved by adding an argument to the description
field:
srcset
{
guillotine {
query(query: "displayName = 'Lynx'") {
... on com_example_myproject_Animal {
data {
description(processHtml: {imageWidths: [200, 500]}) {
processedHtml
}
}
}
}
}
}
This time, you should find the '<img>' element will include srcset with links to a 200w and 500w respectively.
<img alt="kurt-cotoaga-huXZH43-qiw-unsplash.jpg"
src=".../block-768-768/lion-cub.jpg?filter=..."
data-image-ref="b1cf729f-2191-4f03-bdf9-95c7e9076e5a"
srcset=".../block-200-200/lion-cub.jpg?filter=... 200w,.../block-500-500/lion-cub.jpg?filter=... 500w"
style="width:100%" />
URLs about above were shortened for readability |
Image details
If you prefer to handle all details related to images yourself, or optimize the markup further, you may access more details about the image - like you did for links.
{
guillotine {
query(query: "displayName = 'Lynx'") {
... on com_example_myproject_Animal {
data {
description {
images {
style {
name
aspectRatio
filter
}
}
}
}
}
}
}
}
You can extract the properties that you specified in your styles.xml
previously:
...
"images": [
{
"style": {
"name": "editor-style-grayscale",
"aspectRatio": "1:1",
"filter": "hsbcolorize(0x000000); grayscale(); border(6, 0x000000)"
}
}
]
...
Macros
In the headless context, the client is responsible for rendering of your content. In the case of macros - this gets obvious.
In the processed HTML, each macro is represented by an <editor-macro>
element containing a unique reference key. As for links and images, this reference can be used to access detailed data fields for the macro.
{
guillotine {
query(query: "displayName = 'Lynx'")
{
... on com_example_myproject_Animal {
data {
description {
processedHtml
macrosAsJson
}
}
}
}
}
}
...
"macrosAsJson": [
{
"ref": "9b983010-9bb3-4b96-8200-92d43f9c2140",
"name": "info",
"descriptor": "com.enonic.app.panelmacros:info",
"config": {
"info": {
"body": "The lynx inhabits high altitude forests with dense cover of shrubs, reeds, and tall grass.",
"header": "Habitats"
}
}
}
]
...
To access specific macros and fields, like for content type may use the full GraphQL schema. Try the following:
If you used a different macro than the "Info" macro, the query must be updated to match the specific macro. |
{
guillotine {
query(query: "displayName = 'Lynx'")
{
... on com_example_myproject_Animal {
data {
description {
processedHtml
macros{
ref
descriptor
name
config {
info {
header
}
}
}
}
}
}
}
}
}
This time, you only get the explicit fields you were looking for
...
"macros": [
{
"ref": "2f7601da-e586-414e-a387-b2e76731f4bb",
"descriptor": "com.enonic.app.panelmacros:info",
"name": "info",
"config": {
"info": {
"header": "Habitats",
}
}
}
]
...
By accessing the macro config, you are able to down drill into every single field, accessing all the details you actually you need.
That’s a wrap for rich text handling!