Extending the Contentful web app

A step-by-step guide to building editor extensions

Extensions overview

One of the things that make Contentful such a powerful tool in the hands of developers is its embrace of structured content: you are free to decide not only what fields should make up an entry, but also in what format these fields should be stored - be it text, number, boolean, JSON object, etc. And since different kinds of content call for different input methods, we provide a number of editing extensions for each field type - enabling editors to enter geo-coordinates of UFO sightings just as easily as ingredients for the Chèvre salad recipe.

modelling-guide-06

The Appearance settings in the Content Model section display editor extensions available for a chosen field type

Here is an example from the Contentful demo space, illustrating how editor extensions work in practice. In the attached illustration, the Product name field is assigned a single line input field, the Slug comes with an automatic slug generator, and the Description field is configured to work with a Markdown text editor. You can also choose to swap these extensions for the alternative ones, say, add a multi-line text editor or only allow users select values from a dropdown list. Assigning new extensions would change the look of the entry in the web app, but the underlying data structure and API responses are not affected by them.

With the launch of the UI Extensions SDK, you are no longer confined to a set of default extensions but instead can opt for creating your custom extension. From adding new interface elements, to pulling in external data, to building micro-applications capable of complex data transformations - the possibilities are endless. We documented some interesting examples in the blog post announcing the official launch of the SDK.

This guide will provide you with step-by-step instructions to building your first editor extension. Some of the editors using Contentful have admitted that nothing would make them happier than introducing a WYSYWIG text editor into the web app, so we will use an example of integrating AlloyEditor for this guide. The usual disclaimers apply, mixing up your content with the presentation code is a big no-no for cross-platform projects!

Getting started

The most convenient way to upload and manage editor extensions is via the contentful-extension command line tool. You can install it with the following commands:

1
$ npm install -g contentful-extension-cli

Then, it's time to authenticate yourself. You will need an ID of the space where you will use editor extensions and a valid CMA token (obtaining a token). If you plan to deploy the extension to multiple spaces, repeat the process with each space. Save these credentials in the environment variable (or add them directly to the Makefile):

1
2
$ export SPACE=177udz22h888
$ export CONTENTFUL_MANAGEMENT_ACCESS_TOKEN=superSecretKey

Building an editor extension

At the very least, an extension requires two files:

  • extension.json describing the properties of an extension
  • index.html containing markup code and logic

To enable the extension to communicate with the web app, you need to include the contentful-extension-api library in your app. You also want to include default Contentful styles and any other dependencies in your app file. A barebones index.html file structure might look like this:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
<!doctype html>
<html lang="en">
  <head>
    <meta charset="UTF-8"/>
    <title>Sample Editor Extension</title>
    <!-- Contentful's default styles -->
    <link rel="stylesheet" href="https://contentful.github.io/ui-extensions-sdk/cf-extension.css">
    <!-- UI Extensions SDK -->
    <script src="https://contentful.github.io/ui-extensions-sdk/cf-extension-api.js"></script>
    <!-- Alloy includes -->
    <link href="https://contentful.github.io/extensions-samples/libs/alloy-editor/assets/alloy-editor-ocean-min.css" rel="stylesheet">
    <script src="https://contentful.github.io/extensions-samples/libs/alloy-editor/alloy-editor-all-min.js"></script>
  </head>
  <body>
    <div id="content"></div>
    <script>
    <!-- Your extension code here -->
    </script>
  </body>
</html>

The extension-api library exposes the contentfulExtension.init() method. This is the main entry point for all extension-related code. If you require the script from the web without any module system, your method will look like this:

1
2
3
4
window.contentfulExtension.init(function (extension) {
  var value = extension.field.getValue()
  extension.field.setValue("Hello world!")
})

For more complex applications, it makes sense to use module loaders like CommonJS / RequireJS. In this case, our code will look as follows:

1
2
3
4
var contentfulExtension = require('contentful-ui-extensions-sdk')
contentfulExtension.init(function (extension) {
  /* ... */
})

Going back to the example of the AlloyEditor extension, we begin by initializing the extension:

1
2
3
4
5
6
7
8
9
// Reference to the extension API
var cfExt = window.contentfulExtension

// Grab the alloy editor instance
const AlloyEditor = window.AlloyEditor

// `init` method passes an instance of the SDK to the callback giving us access to all its features
cfExt.init(function (ext) {
  var currentValue

Then add boilerplate code to handle DOM-resizing and initialize an instance of an editor:

1
2
3
4
5
6
  // Resizes the extension iframe whenever the size of the document changes.
  ext.window.startAutoResizer()

  // Some boilerplate Alloy editor function.
  const editor = AlloyEditor.editable('content')._editor
  editor.setData(ext.field.getValue())

Next, add a callback for detecting changes made by collaborators and a method for writing a field value:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
  // When the value of the field is changed, the callback is fired with the new value.
  ext.field.onValueChanged(function(value) {
    if (value !== currentValue) {
      currentValue = value
      editor.setData(value)
    }
  })

  // Compare the value of the field with the one fetched from the web app
  editor.on('change', function() {
    const value = editor.getData()

    if (currentValue !== value) {
      currentValue = value
      ext.field.setValue(value)
    }
  })
})

Your index.html file is now ready for use. Take a moment to check out the AlloyEditor example and learn about how the rest of the code is organized or go straight to the next step.

Uploading an extension to Contentful

You can host editor extensions on the Contentful platform as long as your index.html file is smaller than 200 KB. Since more complex widgets almost often are bigger than that, you will have to host them outside of Contentful. Any platform - S3, Heroku or your own corporate server - will do, as long as it supports CORS policy and is available through HTTPS.

Before uploading the extension, you need to prepare a descriptor file. In your extension.json file, define key attributes used to create the extension:

  • name - extension name
  • fieldTypes - list of compatible field types (e.g. symbol, text, number, boolean, object, etc.)
  • src or srcdoc - includes the URL for the extension bundle or a path to the serialized extension bundle
  • sidebar - determines the location of the extension in the web app, if set to true will be rendered in the sidebar
  • id - id property used in the development process

The descriptor file for the Alloy editor will look like this:

1
2
3
4
5
6
{
  "id": "alloy",
  "name": "Alloy WYSIWYG Editor",
  "src": "./index.html",
  "fieldTypes": ["Text"]
}

Now navigate to the folder with the extension code and register the extension with the Contentful API to make the web app aware of its existence:

1
$ contentful-extension create --space-id $SPACE

If you modify the extension, use the update sub-command to push your changes to the Contentful web app. For that, use:

1
contentful-extension update --space-id $SPACE --force

Testing from your local development environment

It is a common practice to develop and test extensions from your local environment to avoid redeploying it each time you need to preview your changes. To do that, first run your extension on a local server as follows:

1
python -m SimpleHTTPServer 3000

This will allow your extension to be available at http://localhost:3000/. Then, you may update your extension again by overriding its src via the following command:

1
2
contentful-extension update --space-id $SPACE --force \
  --src http://localhost:3000/

Note: since Contentful runs in an HTTPS environment, running this requires to disable temporarily the security checks from your web browser ("Load unsafe scripts" in Chrome for example).

Important: once finished with debugging, you should redeploy your extension without the src argument override to let it run from Contentful's servers or any other external hosting solution you chose.

Configuring a field to use an extension

The final step is to assign your extension to the particular field where you want to use it. Navigate to the Content Model section, select the appropriate content type and open field appearance settings - your extension should now be available there.

UI Extensions

The newly created editor extension is now available in the Appearance settings view

And just like that, your editors can go back to creating texts the old fashioned way - with familiar-looking tables, inlined images, and aligned paragraphs:

Alloy editor

If you plan to use the extension across several fields or content types, then you have to repeat these steps for each field. We also included an FAQ section in the Github repo, so if you stumble along the way, make sure to consult it.

Getting more out of the SDK

The AlloyEditor is just one of the several examples we have included in the newly released UI Extensions SDK. Other examples illustrate how to visualize a chessboard game, work with videos hosted on the Wistia platform, or tap into Yandex translation services to automate your content localization. We are curious to see what kind of new things you will build and encourage you to make pull requests to the UI Extensions SDK repo showcasing your editor extensions. Have fun!

Discover more Contentful.

If you enjoyed this article, you might like our product as well.

What our product does

Take a look at the API capabilities, delivery infrastructure and friendly editing environment.
See features

What our clients built

From apps and sites to digital signage and iBeacon campaigns — we deliver content everywhere.
See real-life examples