Integrating your own content editing features

Imagine your content editors want to write their blog posts in a simple markup language or you want to use an external service for storing and managing your assets. In situations like these, Custom elements help you with extending the built-in functionality of the Kentico Cloud UI and take the content editing experience to another level.

Premium feature

Custom elements require a Professional plan or higher. See Pricing for more details.

Table of contents

    A Custom element is essentially a small HTML application that exists in a sandboxed <iframe> and interacts with the Kentico Cloud app through the Custom Elements API. So, apart from using the default content type elements such as Rich text or Linked items element, you can create your own elements according to your needs.

    The implementation of Custom elements depends completely on your use-case scenario. It can be implemented as a simple HTML app mixed with a bit of JavaScript or an advanced app based on your favorite framework.

    To get you started, follow a step-by-step guide on how to extend the Kentico Cloud app with a basic color picker. This Custom element allows you to choose a color from the palette and sets it as a HEX string. The color picker used in this guide is based on one of our sample Custom elements.

    1. Creating a Custom element

    Prepare an HTML web page containing your Custom element specification while utilizing the Custom Elements API methods.

    Custom elements require the latest SDK version 

    When using Custom elements in your project, make sure you have the latest SDK version so that your app can display the Custom elements correctly.

    Including the Custom Elements API

    The Custom Elements API is a JavaScript API that allows you to create Custom elements for Kentico Cloud. To use the API, you need to include it in your HTML source code.

    • HTML
    <script src="https://app.kenticocloud.com/js-api/custom-element.js"></script>
    <script src="https://app.kenticocloud.com/js-api/custom-element.js"></script>

    Adding CSS styles

    By including Kentico Cloud default styles, you can make your Custom element look consistent with the rest of the UI. 

    The public GitHub repository contains:

    • {~custom-element.css~} – CSS stylesheet
    • {~kentico-icons-v1.6.0.woff~} – Font file
    • {~examples.html~} – An HTML page containing the implementation details and an HTML markup of some of the basic elements

    We recommend you clone the files and host them locally yourself. {~kentico-icons-v1.6.0.woff~} needs to be hosted in the same directory as the CSS stylesheet to be properly linked.

    Alternatively, you can link the CSS stylesheet in your HTML source code as seen in the code below.

    • HTML
    <link rel="stylesheet" type="text/css" href="https://kentico.github.io/custom-element-samples/shared/custom-element.css">
    <link rel="stylesheet" type="text/css" href="https://kentico.github.io/custom-element-samples/shared/custom-element.css">

    Getting the element value

    Securing your Custom element

    When displaying the Custom elements in the Kentico Cloud UI, we recommend you always secure your implementation.

    You can use the {~X-frame-option~} HTTP response header to specify an allowed origin, in this case, {~https://app.kenticocloud.com~}. For more details, see MDN Web Docs.

    When initializing a Custom element, you need to set up its value as seen in the code below.

    • JavaScript
    CustomElement.init((element, _context) => { // Set up the element with initial value setupColorPicker(element.value); });
    CustomElement.init((element, _context) => { // Set up the element with initial value setupColorPicker(element.value); });

    Specifying the JSON parameters

    You can make certain properties of the Custom element configurable by specifying the JSON parameters. See the init method in the API Reference for more details.

    • JavaScript
    CustomElement.init((element, _context) => { // Set up the Custom element using JSON parameters setupColorPicker(element.config.outputFormat); });
    CustomElement.init((element, _context) => { // Set up the Custom element using JSON parameters setupColorPicker(element.config.outputFormat); });

    Setting the element value

    When the element value changes, you need to save the value to the Kentico Cloud UI. See the specification of the {~setValue~} method in our API Reference.

    • JavaScript
    CustomElement.setValue("#" + color);
    CustomElement.setValue("#" + color);

    Specifying the height of the element

    The height value of the element can be set using the {~setHeight~} method. We strongly recommend you set the value in the shortest time possible to avoid screen flickering while scrolling and other related issues.

    If you need to load some time-consuming resources in your Custom element and then set the final height of the element, try first setting at least some default height value and then update the final height once the user interacts with the Custom element. By doing so, you will not experience any problems when loading the content item containing the Custom element.

    When the height value is set correctly, you won't see the scrollbar when displaying the element in the UI.

    You can find an example of setting a dynamic height and reacting to the window 'resize' events in one of our sample Custom elements, Markdown editor.

    • JavaScript
    CustomElement.setHeight(height);
    CustomElement.setHeight(height);

    Disabling changes on published content

    Use the {~onDisabledChanged~} method to define what should happen with the Custom element when it's disabled for editing. This way, you can prevent any unexpected behavior that might result from trying to update such Custom element.

    When disabled, the element is in the read-only mode. This means the element is visible in a content item but cannot be changed. Custom element can be disabled when, for example, the content is published, when displaying the element in one of the revisions, or when a user doesn't have sufficient permissions to edit content.

    See the method specification in our API Reference.

    • JavaScript
    CustomElement.onDisabledChanged(updateDisabled);
    CustomElement.onDisabledChanged(updateDisabled);

    Final HTML code

    You can see the completed HTML code below or in our sample elements repository on Github.

    • HTML
    <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Color Picker</title> <!-- Include jQuery library --> <script src="https://cdn.jsdelivr.net/npm/jquery@3.3.1/dist/jquery.min.js"></script> <script src="https://kentico.github.io/custom-element-samples/ColorPicker/color-picker.min.js"></script> <link rel="stylesheet" href="https://kentico.github.io/custom-element-samples/ColorPicker/color-picker.min.css"> <!-- Include the Custom Elements API--> <script src="https://app.kenticocloud.com/js-api/custom-element.js"></script> <!-- Custom element CSS styles --> <style> /* We recommended you always set margin to zero to avoid problems when displaying the element in UI */ body { margin: 0; } div.color-picker-box { width: 100%; padding: 30px 0 30px 0; text-align: center; z-index: 1; } .color-picker.static { display: inline-block !important; position: static !important; top: 0 !important; left: 0 !important; } .disabled_overlay { position: fixed; z-index: 10; cursor: not-allowed; top: 0; left: 0; width: 100%; height: 100%; opacity: 0; } </style> </head> <body> <!-- Custom element HTML --> <div class="disabled_overlay"></div> <div class="color-picker-box"> <section id="color-picker"></section> </div> <!-- Custom logic of the Custom element --> <script> var isDisabled = false; function updateDisabled(disabled) { isDisabled = disabled; if (disabled) { $(".disabled_overlay").show(); } else { $(".disabled_overlay").hide(); } } function setupColorPicker(hexColorValue) { const container = document.querySelector("#color-picker"); const picker = new CP(container, false, container); picker.self.classList.add("static"); if (!!hexColorValue) { picker.set(hexColorValue); container.parentNode.style.backgroundColor = hexColorValue; } picker.on("change", function (color) { container.parentNode.style.backgroundColor = "#" + color; if (!isDisabled) { // Send updated color to Kentico Cloud CustomElement.setValue("#" + color); } }); picker.enter(); } function updateSize() { // Update the Custom element height in the Kentico Cloud UI const height = $("html").height(); CustomElement.setHeight(height); } function initCustomElement() { try { CustomElement.init((element, _context) => { // Setup with initial value and disabled state setupColorPicker(element.value); updateDisabled(element.disabled); updateSize(); }); // React when the disabled state changes (e.g. when publishing the item) CustomElement.onDisabledChanged(updateDisabled); } catch (err) { // Initialization with the Custom elements API failed // (page displayed outside of the Kentico Cloud UI) console.error(err); setupColorPicker(); updateDisabled(true); } } initCustomElement(); </script> </body> </html>
    <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Color Picker</title> <!-- Include jQuery library --> <script src="https://cdn.jsdelivr.net/npm/jquery@3.3.1/dist/jquery.min.js"></script> <script src="https://kentico.github.io/custom-element-samples/ColorPicker/color-picker.min.js"></script> <link rel="stylesheet" href="https://kentico.github.io/custom-element-samples/ColorPicker/color-picker.min.css"> <!-- Include the Custom Elements API--> <script src="https://app.kenticocloud.com/js-api/custom-element.js"></script> <!-- Custom element CSS styles --> <style> /* We recommended you always set margin to zero to avoid problems when displaying the element in UI */ body { margin: 0; } div.color-picker-box { width: 100%; padding: 30px 0 30px 0; text-align: center; z-index: 1; } .color-picker.static { display: inline-block !important; position: static !important; top: 0 !important; left: 0 !important; } .disabled_overlay { position: fixed; z-index: 10; cursor: not-allowed; top: 0; left: 0; width: 100%; height: 100%; opacity: 0; } </style> </head> <body> <!-- Custom element HTML --> <div class="disabled_overlay"></div> <div class="color-picker-box"> <section id="color-picker"></section> </div> <!-- Custom logic of the Custom element --> <script> var isDisabled = false; function updateDisabled(disabled) { isDisabled = disabled; if (disabled) { $(".disabled_overlay").show(); } else { $(".disabled_overlay").hide(); } } function setupColorPicker(hexColorValue) { const container = document.querySelector("#color-picker"); const picker = new CP(container, false, container); picker.self.classList.add("static"); if (!!hexColorValue) { picker.set(hexColorValue); container.parentNode.style.backgroundColor = hexColorValue; } picker.on("change", function (color) { container.parentNode.style.backgroundColor = "#" + color; if (!isDisabled) { // Send updated color to Kentico Cloud CustomElement.setValue("#" + color); } }); picker.enter(); } function updateSize() { // Update the Custom element height in the Kentico Cloud UI const height = $("html").height(); CustomElement.setHeight(height); } function initCustomElement() { try { CustomElement.init((element, _context) => { // Setup with initial value and disabled state setupColorPicker(element.value); updateDisabled(element.disabled); updateSize(); }); // React when the disabled state changes (e.g. when publishing the item) CustomElement.onDisabledChanged(updateDisabled); } catch (err) { // Initialization with the Custom elements API failed // (page displayed outside of the Kentico Cloud UI) console.error(err); setupColorPicker(); updateDisabled(true); } } initCustomElement(); </script> </body> </html>

    {~color-picker.html~} is now ready to use.

    2. Secure hosting

    Custom elements are HTML applications loaded in an {~<iframe>~}. They need to be hosted so the browser can fetch the file. The hosting of your Custom elements source code needs to be done on your end.

    • Always use a secured connection (HTTPS) for your hosted code URL. For example, {~https://example.com/custom.html~}.
    • If you first want to test your implementation locally, you need to generate a self-signed HTTPS certificate.

    Sandboxing the <iframe>

    The HTML5 sandbox attribute is used on the iframe to separate an extension from the Kentico Cloud app. The following sandbox flags are enabled:

    • allow-forms – allows form submission
    • allow-scripts – allows JavaScript to run inside the iframe

    If no options are specified for the sandbox then the iframe can only display basic HTML.

    Learn more about the sandbox attribute and the restrictions it brings at W3Schools.

    3. Displaying a Custom element in Kentico Cloud

    After implementing a Custom element and providing hosting for your source code, you need to add the element to a content type in the UI.

    1. From the app menu, choose Content models {@icon-content-models@}.
    2. Choose an existing content type or create a new one by clicking Create new.
    3. Drag & drop a Custom element element into the element area and give it a name.
    4. Add your hosted code URL.
    5. (Optional) Fill in the JSON parameters.
    6. Click Save.

    A few things to keep in mind:

    • When adding your self-hosted code URL, you need to use an HTTPS protocol. For example, {~https://example.com/hello.html~}.
    • If you set a Custom element as required, Kentico Cloud will test if the requirement is fulfilled by checking if the element contains any value other than {~null~}. Everything else is considered a valid value and thus won't generate an error message when an item containing the element gets published.

    JSON parameters in the UI

    To have your Custom element reusable and configurable, fill in the JSON parameters field. By applying JSON to the element, you can, for example, use the element in different content types or have a customizable layout for the element. Leave the field empty if you don't need it.

    Warning: Never store your API keys or any other sensitive data in the JSON parameters field, it's not secure!

    In the example below, JSON is used to specify the output format.

    How your Custom element might appear inside a content type.

    4. Final result

    After creating a content item containing the Custom element, the result will be similar to the one below.

    How your Custom element might appear inside a content item.

    Sample Custom elements & devkit

    We have a variety of sample Custom elements prepare for you to use. You can see how they look in action in the Custom Elements Gallery or look at their code in the custom elements GitHub repository. By using these prepared elements, you won't need to start implementing your UI extensions completely from scratch.

    The examples are open source so they can be fit to your specific needs. If you plan on using these sample elements in your own production project, we recommend you clone the repository. By doing so, you will not be affected by the possible changes made to the custom elements in the future. Find inspiration in the existing sample custom elements at GitHub and create your own.

    You can also take advantage of our Custom elements devkit, which we created to help you prepare your Custom elements for production. The devkit includes a Kentico-Cloud-like wireframe and mocked API to enable a seamless debugging experience.

    What's next?

    • Check out our Custom Elements API specification to see all the available API methods.
    • See how you can use custom elements for integration with 3rd party services such as Shopify and Optimizely.