Skip to main content

Editing Text in the Headless CreativeEngine

In this example, we want to show how to set up text editing in the Headless CreativeEngine.

Depending on your use cases, text editing can require a little more complicated setup, because the focus for the user's keyboard input might need to be managed between the CreativeEngine itself and your UI components.

Explore a full code sample of the integration on CodeSandbox or view the code on GitHub.

Text Edit Mode#

The Engine always operates in one of three EditModes:

  • Transform: In this mode you can transform blocks in the scene by moving them around, selecting, or resizing them. This is the default mode.
  • Crop: This mode is used when cropping image blocks.
  • Text: Then double-clicking on a text block, the engine switches to this mode to let you edit the text content of a text block.

You can query and set the EditMode using the EditorAPI's getEditMode() and setEditMode(mode) methods. Certain interactions will cause the engine to switch the mode by itself (such as double-clicking on a text block).

You get notified of changes in the EditorState using the onStateChanged method of the EditorAPI. Inside the callback, use getEditMode() to know the current mode.

Text Editing Focus#

When the engine enters Text mode, it will focus the browser's input on the text content of the block you are editing. From here, several things can happen:

  • The user presses ESC, clicks somewhere on the canvas outside of the text block, or Text mode is ended via a call to setEditMode('Transform'). In this case, the browser will blur the text input of the engine, and you are back in the Transform mode.
  • The user focuses a text input field, number input field, text area or a contentEditable element outside of the canvas. In this case the engine will stay in Text mode, but will no longer have focus. Keyboard input will now go to the input field that has received focus.
  • The user clicks on a blank space or any focusable DOM element, that does not require keyboard input.

This behavior should be sufficient for most use cases. If your requirements are more complicated, we provide a way to customize what's happening through events.

CreativeEngine DOM Events#

There are currently two types of custom DOM events that the CreativeEngine dispatches on the canvas element. You can add a lister for it there or on any of its parent elements.


When the text input in the engine has been blurred, this event will be dispatched. You can call preventDefault() on the event to force focus back to the canvas input. The event.detail property will contain a reference to the element that has received focus (if available, otherwise it's null). You can use that element during the decision whether to call preventDefault().

A use case for this could be to prevent disabling the text-input heuristic, forcing refocus even if the user clicked on a text input, or to call editor.setEditMode('Transform') to exit the text edit mode whenever the input is blurred.


Just before the engine refocuses its text input, you can call preventDefault() on this event to prevent this from happening. This event also contains currently focused element (or null) it in its event.detail property.

A use case for this would be to treat a particular input element as if it were a text input and let it keep keyboard focus after it's been focused.

import CreativeEngine from '';
const config = {
baseURL: ''
CreativeEngine.init(config, document.getElementById('cesdk_canvas')).then(
async (instance) => {
instance.editor.onStateChanged(() => {
console.log('EditMode is ', instance.editor.getEditMode());
document.addEventListener('cesdk-blur', (event) => {
const relatedTarget = event.detail;
if (focusShouldStayOnEngine(relatedTarget)) {
} else if (engineShouldExitTextMode(relatedTarget)) {
function focusShouldStayOnEngine(newActiveElement) {
// When clicking on blank space, don't blur the engine input
return newActiveElement == null;
function engineShouldExitTextMode() {
return false;
document.addEventListener('cesdk-refocus', (event) => {
const relatedTarget = event.detail;
if (focusShouldStayOnUI(relatedTarget)) {
function focusShouldStayOnUI(newActiveElement) {
// User might have clicked a button that opens a dialog
// Afterwards we want an input in the dialog to receive focus
return newActiveElement?.id === 'open-dialog';
await instance.scene.loadFromURL(