---
url: 'https://metadrop.net/en/articles/ckeditor-widgets-an-alternative-to-paragraphs'
title: 'CKEditor widgets: an alternative to Paragraphs?'
author: 'Nacho Díaz'
date: '2020-04-20T12:26:07+00:00'
updated: '2026-06-09T13:20:15+00:00'
type: article
summary: 'Whenever users require to be able to enter predefined HTML snippets such as quotations, tabs or even simple layouts, our first option is usually to create new fields or paragraphs. However, there is an alternative that in some cases is better and easier: CKeditor widgets.'
tags:
  - 'Paragraphs (Drupal module)'
  - CKEditor
  - 'Site building'
  - HTML
  - JavaScript
image: 'https://metadrop.net/sites/default/files/styles/full/public/imce/image/article/CTA_sample.png.webp?itok=MC2e2ZXx'
published: true
og:
  determiner: Automatic
  site_name: Metadrop
  'image:url': 'https://metadrop.net/sites/default/files/styles/full/public/imce/image/article/CTA_sample.png.webp?itok=MC2e2ZXx'
  'image:type': image/png
  'image:width': '936'
  'image:height': '386'
  'image:alt': 'CKEditor widgets: an alternative to Paragraphs?'
  street_address: 'Calle Manuel Luna, 12, 3 Dcha'
  locality: Madrid
  region: Madrid
  postal_code: '28020'
  country_name: España
  email: hola@metadrop.net
  phone_number: '910053180'
schema:
  '@context': 'https://schema.org'
  '@graph':
    -
      '@type': Article
      '@id': 'https://metadrop.net/en/articles/ckeditor-widgets-an-alternative-to-paragraphs#article'
      name: 'CKEditor widgets: an alternative to Paragraphs?'
      headline: 'CKEditor widgets: an alternative to Paragraphs?'
      description: "Whenever users require\_to be able to enter predefined HTML snippets such as quotations, tabs or even simple layouts, our first option is usually to create new fields or paragraphs. However, there is an alternative that in some cases is\_better and easier: CKeditor widgets."
      about:
        - 'Paragraphs (Drupal module)'
        - CKEditor
        - 'Site building'
        - HTML
        - JavaScript
      image:
        '@type': ImageObject
        url: 'https://metadrop.net/sites/default/files/styles/full/public/imce/image/article/CTA_sample.png.webp?itok=MC2e2ZXx'
        width: '936'
        height: '386'
      datePublished: '2020-07-15T15:26:07+0200'
      dateModified: '2026-06-09T15:20:15+0200'
      author:
        '@type': Person
        name: 'Nacho Díaz'
      publisher:
        '@type': Organization
        '@id': 'https://metadrop.net/#organization'
      mainEntityOfPage: 'https://metadrop.net/en/articles/ckeditor-widgets-an-alternative-to-paragraphs'
    -
      '@type': Organization
      '@id': 'https://metadrop.net/#organization'
      url: 'https://metadrop.net/'
      name: Metadrop
      sameAs:
        - 'https://www.drupal.org/metadrop'
        - 'https://twitter.com/metadrop'
        - 'https://asociaciondrupal.es/partner/metadrop'
        - 'https://www.linkedin.com/company/metadrop'
      logo:
        '@type': ImageObject
        url: 'https://metadrop.net/themes/custom/mdrop_radix/logo-metadrop-500-500.jpg'
        width: '500'
        height: '500'
    -
      '@type': ItemPage
      '@id': 'https://metadrop.net/en/articles/ckeditor-widgets-an-alternative-to-paragraphs'
      breadcrumb:
        '@type': BreadcrumbList
        itemListElement:
          -
            '@type': ListItem
            position: 1
            name: Home
            item: 'https://metadrop.net/en'
          -
            '@type': ListItem
            position: 2
            name: 'Expert Drupal & Tech Articles'
            item: 'https://metadrop.net/en/articles'
      publisher:
        '@type': Organization
        '@id': 'https://metadrop.net/#organization'
---
 1. [Articles](https://metadrop.net/en/articles)
 
  

# CKEditor widgets: an alternative to Paragraphs?

Wednesday, July 15, 2020

 

 



Whenever users require to be able to enter predefined HTML snippets such as quotations, tabs or even simple layouts, our first option is usually to create new fields or paragraphs. However, there is an alternative that in some cases is better and easier: CKeditor widgets.



   



It is quite common for publishers to want to be able to insert predefined HTML fragments (small components and even layouts) into their content.

In some websites, such as online media outlets, these pieces of code can grow in number until they become a full-fledged library of simple and reusable components: quotes, information boxes, call-to-action, etc.

The first impulse of a Drupalista might be to use a combination of fields + field groups and/or paragraphs, **overlooking a solution that in many occasions is simpler and easier to maintain**, just as solid and that provides great advantages for the end-user: CKeditor widgets.

## What is a widget?

In view of the need to offer a set of predefined formats, it is common to use CKeditor styles, which allow us to associate an HTML element and a class (or ID, or even inline CSS). When clicking on this style, CKeditor tries to convert the element in focus.

A widget is a more complete and advanced solution to this problem:

1. **Allows (and keeps) any HTML structure.**
    While the style is applied to a single element, a widget allows any markup. In addition, CKEditor treats the widget as a whole and keeps its structure intact. This avoids a common problem where an applied style can be unintentionally altered when editing text, resulting in malformed or broken HTML that CKEditor filters out and removes
2. **It is configurable, draggable and usable.**
    While a style can often be invisible, the widget is clearly distinguished by its yellow outline and drag icon.
3. **It can be prepopulated.**
    Styles are applied to the text and can't be prepopulated to show the expected content to the user. Widgets can be initialized already completed, telling the user what the component looks like and what to enter.
4. **You can contextually limit the allowed HTML tags.**
    This is extremely useful to give power to the user without resulting in malformed, semantically incorrect HTML or altering our design. You can also indicate a different configuration for each editable zone.

## A simple widget case: call-to-action.

Let's say **our client wants to be able to freely insert a call-to-action (CTA)** like this one:

Initially, this CTA is a good candidate to be implemented as a paragraph divided into three fields: a plain text for the title, a formatted text for the body and a link field for the button.

The result could be as follows:

However, this example is a good candidate to be implemented as a widget instead, as long as we can confirm that it is a purely visual component that does not need to interact with Drupal (e.g. the link does not need to autocomplete, reference a Drupal entity, etc...).

Next, we will show how this same component would be developed as a widget.

### 1 - Create the Drupal plugin

The first step is to [create a module](https://www.drupal.org/docs/8/creating-custom-modules ""Creating custom modules" on drupal.org") and, within it, a CKeditor plugin. For the purpose of this tutorial we are going to use the autogenerated code through Drupal Console or Drush (from version 9):

```bash
drupal generate:plugin:ckeditorbutton
drush generate plugin-ckeditor

```


If you use Drupal Console, beware: in certain versions some values need to be corrected. You can also follow [this article](https://opensenselabs.com/blog/tech/creating-custom-ckeditor-plugin-drupal-8 "Article "Creating a custom CKEditor plugin in Drupal 8"") if you prefer to do it by hand.

In its most minimalist implementation, the Drupal plugin simply has to report the icon address and the plugin.js file, and the rest is already CKeditor's responsibility. This will be enough for us.

It's time to go to our text editor settings and **drag the new button to our toolbar**.

### 2 - Create the CKeditor widget

At this point we have a button that does nothing. It's time to turn it into a CKeditor widget that allows us to insert our component.

CKEditor has an[ excellent tutorial](https://ckeditor.com/docs/ckeditor4/latest/guide/widget_sdk_tutorial_1.html "Creating a Simple CKEditor Widget (Part 1)") where it tells step by step how to create a widget so here we are going to focus only on some implementation details.

The first thing we need to know is that **a widget is a special type of CKeditor plugin that depends on the 'widget' plugin and doesn't implement the usual methods**. Hence, we need to add the dependency and (if we've used the generator) remove the call to `editor.addCommand()`.

But, most importantly, to integrate correctly with Drupal **we have to make an explicit call to editor.ui.addButton()**, something that doesn't appear in the official tutorial and that can be the source of more than one headache.

```javascript
CKEDITOR.plugins.add( 'cta_widget', {
    // Necesitarás añadir esta dependencia:
    requires: 'widget',

    // Comprueba que tiene el mismo id que tu botón:
    icons: 'cta_widget',

    init: function( editor ) {
        
        // Añade esta llamada dentro de init
        editor.ui.addButton('cta_widget', {
            label: 'CTA Widget',
            id: 'cta_widget',
            command: 'cta_widget',
            icon: this.path + 'icons/cta.png'
        });
    }
});
```

Once this is done, we only have to configure the widget using the `editor.widgets.add()` method:

```javascript
editor.widgets.add( 'cta_widget', {
    template: ''
    editables: {},
    allowedContent: ''
    upcast: function( element ) {},
});
```

### 3 - Define the HTML

The `template` property is a string that will define the desired HTML markup. In this string we must include the complete markup, including both editable and non-editable elements. In our case, it could be something like this:

```javascript
template:
    '<div class="cta">' + 
        '<h3 class="cta__title">Insert title here...</h3>' + 
        '<p class="cta__text">Insert your text here</p>' + 
        '<div class="cta__link"><a href="#">Double click to edit link</a></div>' + 
    '</div>',
```

Note how a default text is also provided. This allows us to pre-populate the widget with values that tell the user what kind of information they expect to receive.

If we go to our CKeditor at this point and press the button, this HTML should be inserted and, if we have loaded the CSS correctly, we should see it well styled. However, in this state it is still not editable.

### 4 - Crear editable areas and restrict content

The property 'editables' from `editor.widgets.add()` allows to define an object to **name the different editable areas and allows us to configure them**. In our case, a good configuration is the following:

```javascript
editables: {
    title: {
        selector: '.cta__title',
        allowedContent: 'strong em',
    },
    content: {
        selector: '.cta__text',
        allowedContent: 'strong em mark br; a[!href]',
    },
    link: {
        selector: '.cta__link',
        allowedContent: 'a[!href]',
    }
},
```

As you can see, we're mapping HTML elements like `<h3 class="cta__title">` with entries like `title`, identified by a `selector`. In addition, we are making use of a very **interesting functionality** that is the ability to **define the type of content that each editable zone allows**.

The `allowedFormats` key uses the special syntax of [CKeditor's Allowed Content Rules](https://ckeditor.com/docs/ckeditor4/latest/guide/dev_allowed_content_rules.html "Allowed Content Rules") and allows us to restrict the allowed HTML, a very useful functionality and much more granular than Drupal text formats.

In this case, we are avoiding in the title the use of anything other than `<strong>` and `<em>`, which is usually a quite **common requirement from clients** in title fields. In the body of the text we allow the use of a few more elements and in the button we only allow links.

At this point the **widget should already be functional** and should allow the user to create, edit and even drag and drop it:

At this point, someone might suggest that the `.cta__link` editable element might be an `<a>` instead of a `<div>`. This is true, but it is not possible with the standard Drupal configuration of CKEditor since only the elements defined in [CKEDITOR.$dtd.editable](https://ckeditor.com/docs/ckeditor4/latest/api/CKEDITOR_dtd.html#property-S-editable "CKEditor DTD documentation") can be converted into editable zones.

It is possible to change this configuration to add the elements of type `<a>`, but this exceeds the purpose of this tutorial, so we will leave it as it is for now.

### 5 - The final touch: autedection and permissions

In its current state, the widget still has two possible problems.

On the one hand, if we're using the "Limit allowed HTML tags and correct faulty HTML" filter, it's quite possible that when we save the content or go into "HTML source" mode we'll lose the CSS classes. This is due to the sanitizing of CKEditor we talked about before.

If we were in a standard configuration of CKEditor, it would be enough to add the following configuration to `editor.widgets.add()` and it would add it to CKeditor's whitelist:

```javascript
allowedContent: 'h3(cta__title); div(cta__text, cta__link)',
```

However, **this is not enough** since Drupal implements its own mode of popularizing the CKEditor configuration. Since it seems that at the moment there is no good way to add new rules programmatically, we will have to **add it manually in the configuration of each editor**:

In addition, simply inserting an HTML file identical to our widget **does not mean that it is automatically instantiated**. In other words, in the case of directly copying and pasting the HTML of our component, the user might want to edit it and find that it is not possible.

This is quickly solved by implementing the `upcast()`, method, which is not mandatory, but highly recommended:

```javascript
upcast: function( element ) {
    return element.name == 'div' && element.hasClass( 'cta' );
},
```

Each time it processes the HTML, CKEditor iterates over the HTML element by element and gives us a chance to indicate that we want to initialize our widget. In this case, we're telling CKEditor to try to build the widget if it finds a `<div>` with the `.cta` class. The editor will check to see if this element matches our template, and if it does, it will initialize it.

Our final code will look like this:

```javascript
CKEDITOR.plugins.add( 'cta_widget', {
    // Widget plugin dependency
    requires: 'widget',
    icons: 'cta_widget',
    init: function( editor ) {
        editor.widgets.add( 'cta_widget', {
            template:
                '<div class="cta">' + 
	                '<h3 class="cta__title">Insert title here...</h3>' + 
                    '<p class="cta__text">Insert your text here</p>' + 
                    '<div class="cta__link"><a href="#">Double click to edit link</a></div>' + 
                '</div>',

            editables: {
                title: {
                    selector: '.cta__title',
                    allowedContent: 'strong em',
                },
                content: {
                    selector: '.cta__text',
                    allowedContent: 'strong em mark br; a[!href]',
                },
                link: {
                    selector: '.cta__link',
                    allowedContent: 'a[!href]',
                }
            },
            
            // Not necessary, this is configured in Drupal, but is good to have it here as well.
            allowedContent:
                'h3(cta__title); div(cta__text, cta__link)',
            
            // Not mandatory but allows widget recongnition on copy & paste.
            upcast: function( element ) {
                return element.name == 'section' && element.hasClass( 'cta__widget' );
            },
        });

        // This is needed in Drupal
        editor.ui.addButton('cta_widget', {
            label: 'CTA Widget',
            id: 'cta_widget',
            command: 'cta_widget',
            icon: this.path + 'icons/cta.png'
        });
    }
});


```

### When is a widget a better option than a paragraph?

The main thesis of this article is that **CKeditor widgets can replace paragraphs and fields in some cases**. This may sound bad at first glance: why abandon the capabilities of Drupal 8 to delegate them to a text editor? Aren't there precisely the fields or the paragraphs to avoid the temptation to solve everything in the editor? Won't it introduce more long-term maintenance problems than it eliminates in the short term?

It should be stressed that you should only consider this option in **some very controlled cases** where the following requirements are met:

1. The **HTML piece to be inserted is totally aesthetic** in nature, that is, it does not depend on or interact in any way with Drupal. 
    - Do you need to access the logged-in user, make a reference to an entity, access translations? Forget about widgets.
    - Is it a beautiful or different way of presenting information that could perfectly be shown without any style? **Is it pure front-end?** Then it is a good candidate.
2. **No major changes in its HTML structure are expected**, at least in the medium term: there may be styling changes, but the HTML will remain intact.

If a component meets both requirements, it is highly recommended to consider it as a widget, as it introduces several improvements over the paragraphs.

- **Avoid overloading** our projects with unnecessary fields and paragraphs.
- It provides the editor user with a **real WYSIWYG experience** in which they can check in real time what the result of their work is. This is usually one of the main complaints that editors have about Drupal.
- It allows for **easier and more immediate markup**. To style the HTML and CSS of a paragraph sometimes it is necessary to intervene at several levels, using several templates, hooks or field fomatters. With a CKeditor widget, the markup is written directly in a template. In a certain sense (I repeat: in a certain sense), it is closer to the Component-Driven Design paradigm of Vue, React or Angular.
- Thanks to CKEditor's Allowed Content Rules system, it allows to **define with granularity the HTML allowed and forbidden** in each editable zone in a more detailed and individualized way than Drupal's fields would allow. This allows us to give more power to the editor without introducing the risk of malformed or faulty HTML.
- We haven't had time to go into it in depth, but it **is possible to expose widget configuration options** using the dialog API. This way, we could expose options like adding margin or padding, removing certain editable areas, and so on.

In short, CKEditor widgets are a valid alternative to some component types that we usually solve with Drupal tools like Paragraphs. In the case of purely visual, small components with stable requirements, widgets bring many advantages to the end user and are much easier to develop and maintain.



[Paragraphs (Drupal module)](https://metadrop.net/en/articles?text=Paragraphs%20%28Drupal%20module%29)

[CKEditor](https://metadrop.net/en/articles?text=CKEditor)

[Site building](https://metadrop.net/en/articles?text=Site%20building)

[HTML](https://metadrop.net/en/articles?text=HTML)

[JavaScript](https://metadrop.net/en/articles?text=JavaScript)

 

[Module development and third-party integrations with Drupal](https://metadrop.net/en/services/drupal/integrations " See Module development and third-party integrations with Drupal")

Metadrop connects Drupal to your CRM, ERP, payment, and marketing stack — Salesforce, Microsoft Dynamics, BBVA virtual POS, Workday, and Keepeek — with custom modules and secure, GDPR-compliant APIs.

 

 See more