UI Extensions

Introduction

The Contentful Web App uses components (called appearances) to make the fields of a content type editable. UI Extensions are enabling developers to replace these components with HTML5 applications so the editing experiences of the Contentful Web App can be customized.

Technically speaking a UI Extension lives in a sandboxed <iframe> which interacts with the Contentful Web App through the UI Extensions SDK. This SDK is a proxy to the Content Management API and is acting on behalf of the logged-in user. The code of the UI Extension is fully custom and can either be uploaded to Contentful or be self-hosted.

You can carry on reading this article or take the shortcut and try our interactive installer:

Interactive UI Extension installer

A short text field using a built-in appearance

The image below shows a field title of type short text using the default single line appearance:

A short text field using a custom UI Extension

The image below shows the same field rendered with a UI Extension. The field now renders a diff of the published and draft field value:

The diff UI Extension uses the UI Extensions SDK to retrieve the published field value. Using the jsdiff libary it computes and renders a visual diff of the two values as feedback for an author.

Vanilla HTML5 UI Extension template

As mentioned, UI Extensions essentially are small HTML5 applications. That means there is many ways to implement them – from basic HTML and JavaScript to richer apps based on frameworks such as React, Angular or Vue. It all depends on the individual use case the UI Extension is supposed to solve.

We prepared a vanilla HTML5 UI Extension template implementing the basic lifecycle methods to better understand the concept.

The template renders in the Contentful Web App like this:

The template includes:

  • the UI Extensions SDK library
  • a Contentful look-and-feel by loading our CSS library
  • basic handling of user-generated events (here: keyboard input)
  • basic handling of externally generated events (here: changes introduced by other authors)
  • cleanup of event listeners

template-vanilla.html

<!DOCTYPE html>
<html>
<head>
  <meta charset="utf-8">
  <!-- UI Extensions CSS -->
  <link rel="stylesheet" href="//contentful.github.io/ui-extensions-sdk/cf-extension.css">
  <!-- UI Extensions SDK -->
  <script src="//contentful.github.io/ui-extensions-sdk/cf-extension-api.js"></script>
</head>
<body>
  <!-- Custom markup of the UI Extension -->
  <div class="cf-form-field">
    <input type="text" class="cf-form-input">
    <div class="cf-form-hint">I am a UI Extension.</div>
  </div>

  <!-- Custom logic of the UI Extension -->
  <script>
    'use strict';

    // When UI Extensions SDK is loaded the callback will be executed.
    window.contentfulExtension.init(initExtension);

    function initExtension (extensionsApi) {
      // "extensionsApi" is providing an interface documented here:
      // https://github.com/contentful/ui-extensions-sdk/blob/master/docs/ui-extensions-sdk-frontend.md

      // Automatically adjust UI Extension size in the Web App.
      extensionsApi.window.startAutoResizer();

      var inputEl = document.querySelector('.cf-form-input');

      //  The field this UI Extension is assigned to.
      var field = extensionsApi.field;

      // Callback for changes of the field value.
      var detachValueChangeHandler = field.onValueChanged(valueChangeHandler);
      // Handle keyboard input.
      inputEl.addEventListener('input', keyboardInputHandler);
      // Handle DOM "onbeforeunload" event.
      window.addEventListener('onbeforeunload', unloadHandler);

      // Handler for external field value changes (e.g. when multiple authors are working on the same entry).
      function valueChangeHandler (value) {
        inputEl.value = value || '';
      }

      // Event handler for keyboard input.
      function keyboardInputHandler () {
        var value = inputEl.value;
        if (typeof value !== 'string' || value === '') {
          field.removeValue();
        } else {
          field.setValue(value);
        }
      }

      // Event handler for window unload.
      function unloadHandler () {
        window.removeEventListener('onbeforeunload', unloadHandler);
        inputEl.removeEventListener('input', keyboardInputHandler);
        detachValueChangeHandler();
      }
    }
  </script>
</body>
</html>

Installing UI Extensions into a space

Interactive UI Extension installer

Extensions are installed to a Contentful space through the Content Management API /spaces/:spaceId/extensions endpoint. To make it more convenient we added support for extensions to the Contentful CLI.

Prerequisites

  • Install the Contentful CLI so the contentful command is available
  • Make sure you have an account at Contentful with admin access to a space

Step 1: Prepare the code of a UI Extension

Create an HTML file to hold the code for the UI Extension. To get started we recommend to use the template-vanilla.html we introduced in the section above.

Step 2: Install into a space

Install the UI Extension into a Contentful space using the Contentful CLI:

contentful extension create \
  --name "My UI Extension" \
  --field-types "Symbol,Text" \
  --srcdoc template-vanilla.html

For a full reference read the managing extensions guide.

Assigning UI Extensions to a field

The last step is to assign the extension to a field of a content type. The steps are:

  1. create a Content Type
  2. create a field in the Content Type
  3. head to Settings > Appearance of the field
  4. select the UI Extension your created
  5. create an entry of your Content Type to see your UI Extension in action

The image below shows the Appearance tab under Setting of a field: