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.
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!
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 |
At the very least, an extension requires two files:
extension.json
describing the properties of an extensionindex.html
containing markup code and logicTo 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.
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 namefieldTypes
- 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 bundlesidebar
- determines the location of the extension in the web app, if set to true
will be rendered in the sidebarid
- id property used in the development processThe 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 |
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.
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.
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:
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.
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!