---
url: 'https://metadrop.net/es/articulos/widgets-de-ckeditor-una-alternativa-a-paragraphs'
title: 'Widgets de CKeditor: ¿una alternativa a Paragraphs?'
author: 'Nacho Díaz'
date: '2020-04-20T12:26:07+00:00'
updated: '2026-06-09T13:19:11+00:00'
type: article
summary: 'Cuando los usuarios nos piden poder introducir fragmentos de HTML predefinidos tales como citas, fichas o incluso layouts sencillos, nuestra primera opción suele ser la creación de nuevos campos o paragraphs. No obstante, existe una alternativa que en algunas ocasiones es mejor y más sencilla: los widgets de CKeditor.'
tags:
  - 'Drupal Planet'
  - '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': 'Widgets de CKeditor: ¿una alternativa a 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/es/articulos/widgets-de-ckeditor-una-alternativa-a-paragraphs#article'
      name: 'Widgets de CKeditor: ¿una alternativa a Paragraphs?'
      headline: 'Widgets de CKeditor: ¿una alternativa a Paragraphs?'
      description: "Cuando los usuarios nos piden poder introducir fragmentos de HTML predefinidos tales como citas, fichas\_o incluso layouts sencillos, nuestra primera opción suele ser la creación de nuevos campos o paragraphs. No obstante, existe una alternativa que en algunas ocasiones es mejor y más sencilla: los widgets de CKeditor."
      about:
        - 'Paragraphs (módulo de Drupal)'
        - 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:19:11+0200'
      author:
        '@type': Person
        name: 'Nacho Díaz'
      publisher:
        '@type': Organization
        '@id': 'https://metadrop.net/#organization'
      mainEntityOfPage: 'https://metadrop.net/es/articulos/widgets-de-ckeditor-una-alternativa-a-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/es/articulos/widgets-de-ckeditor-una-alternativa-a-paragraphs'
      breadcrumb:
        '@type': BreadcrumbList
        itemListElement:
          -
            '@type': ListItem
            position: 1
            name: Inicio
            item: 'https://metadrop.net/es'
          -
            '@type': ListItem
            position: 2
            name: 'Artículos expertos sobre Drupal y tecnología'
            item: 'https://metadrop.net/es/articulos'
      publisher:
        '@type': Organization
        '@id': 'https://metadrop.net/#organization'
---
 1. [Artículos](https://metadrop.net/es/articulos)
 
  

# Widgets de CKeditor: ¿una alternativa a Paragraphs?

Miércoles 15 de Julio de 2020

 

 



Cuando los usuarios nos piden poder introducir fragmentos de HTML predefinidos tales como citas, fichas o incluso *layouts* sencillos, nuestra primera opción suele ser la creación de nuevos campos o *paragraphs*. No obstante, existe una alternativa que en algunas ocasiones es mejor y más sencilla: los *widgets* de CKeditor.



   



Es bastante común que los editores quieran poder introducir en sus contenidos fragmentos de HTML predefinidos (componentes e incluso *layouts*).

En algunos sitios web, como la prensa online, estas piezas de código pueden ir sumándose hasta convertirse en auténticas librerías de componentes sencillos y reutilizables: citas, cuadros informativos, call-to-action, etcétera.

El primer impulso de todo drupalista es acudir a combinaciones de fields + field groups y/o *paragraphs*, **pasando por alto una solución que en muchas ocasiones es más sencilla** y fácil de mantener, igual de sólida y que proporciona grandes ventajas de cara al usuario final: los *widgets* de CKeditor.

## ¿Qué es un *widget*?

Ante la necesidad de ofrecer un conjunto de formatos predefinidos es habitual usar los estilos de CKeditor, los cuales nos permiten asociar un elemento HTML y una clase (o ID, o incluso CSS *inline*). Al hacer click en este estilo, CKeditor *intenta* convertir el elemento en foco.

Un *widget* es una solución más completa y avanzada para este problema:

1. **Permite cualquier estructura HTML.**
    Mientras que el estilo se aplica a un único elemento, un *widget* permite cualquier marcado. Además, CKeditor trata al widget como un conjunto y mantiene su estructura intacta. Esto evita un problema común por el que un estilo aplicado se puede alterar involuntariamente al editar el texto, resultando en HTML malformado o roto que CKEditor filtra y elimina.
2. **Es configurable**, **arrastrable y usable.** 
    Mientras que un estilo puede ser muchas veces invisible, el *widget* se distingue claramente por su *outline* amarillo y el icono de arrastrar.
3. **Se puede presentar prerellenados.**
    Los estilos se aplican sobre el texto y no se pueden prerellenar para indicar al usuario qué se espera. Los *widgets* se pueden inicializar ya completos, indicando al usuario cómo es el componente y qué debe introducir.
4. **Se pueden limitar contextualmente las etiquetas HTML permitidas.**
    Esto es extremadamente útil a la hora de otorgar poder al usuario sin que esto resulte en HTML malformado, semánticamente erróneo o que altere nuestro diseño. Se puede indicar además una configuración diferente para cada zona editable.

## Un caso de widget sencillo: call-to-action.

Imaginemos que **nuestro cliente solicita poder insertar libremente un call-to-action (CTA)**  de este estilo:

En un principio, este CTA se soluciona mediante un paragraph dividido en tres campos: un texto sin formato para el título, un texto con formato para el cuerpo y en campo de tipo enlace para el botón:

Sin embargo, este ejemplo es un **buen candidato a ser implementado en su lugar como un widget,** siempre y cuando podamos confirmar que se trata de un componente **puramente visual** que no necesita interactuar con Drupal (por ejemplo, el enlace no necesita autocompletado o referencia a entidades u otro tipo de integración con Drupal).

A continuación, mostraremos sin entrar en muchos detalles cómo se desarrollaría este mismo componente como un *widget*.

### 1 - Crear el *plugin* de Drupal

El primer paso es [crear un módulo](https://www.drupal.org/docs/8/creating-custom-modules) y, dentro de este, un *plugin* de CKeditor. Para el propósito de este tutorial vamos a usar el código autogenerado mediante Drupal Console o Drush (a partir de la versión 9):

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

```

Si usas Drupal Console, cuidado: en ciertas versiones algunos valores necesitan ser corregidos. También puedes seguir [este artículo](https://opensenselabs.com/blog/tech/creating-custom-ckeditor-plugin-drupal-8) si prefieres hacerlo a mano.

En su implementación más minimalista, el plugin de Drupal simplemente tiene que informar de la dirección del icono y el archivo plugin.js, y el resto ya es responsabilidad de CKeditor. Nos bastará con esto.

Es el momento de acudir a la configuración de nuestros editores de texto y **arrastrar el nuevo botón a nuestra barra de herramientas**.

### 2 - Crear el widget de CKeditor

Llegados a este punto tenemos un botón que no hace nada. Es hora de convertirlo en un *widget* de CKeditor que permita insertar nuestro componente.

CKEditor tiene un[ excelente tutorial](https://ckeditor.com/docs/ckeditor4/latest/guide/widget_sdk_tutorial_1.html) donde cuenta paso a paso como crear un widget por lo que aquí nos vamos a centrar solo en algunos detalles.

Lo primero que necesitamos saber es que **un** ***widget*** **es un tipo especial de plugin de CKeditor que a su vez depende del plugin 'widget' y que no implementa los métodos habituales**, por lo que tendremos que añadir la dependencia y (si hemos usado el generador) eliminar la llamada a `editor.addCommand()`.

Pero, sobre todo, para integrar correctamente con Drupal **tenemos que realizar una llamada explícita a editor.ui.addButton()**, algo que no aparece en el tutorial oficial de CKeditor y que puede ser el origen de más de un quebradero de cabeza.

```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'
        });
    }
});
```

Una vez realizado esto, solo nos quedará configurar el *widget* usando el método `editor.widgets.add()`, que tiene esta forma:

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

### 3 - Definir nuestro HTML

La propiedad `template` es una cadena de caracteres que se encargará de definir el marcado HTML deseado. En este *string* debemos de incluir el **marcado completo**, incluyendo tanto los elementos editables como aquellos que no lo serán. En nuestro caso, podría ser algo así:

```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>',
```


En este ejemplo se proporciona también un texto por defecto. Esto nos permite **prerellenar el** ***widget*** con valores que indiquen al usuario qué tipo de información se espera recibir.

Si en este momento acudimos a nuestro CKeditor y pulsamos el botón, se nos debería de insertar este HTML y, si hemos cargado correctamente el CSS, deberíamos verlo bien estilado. Sin embargo, en este estado **todavía no es editable**: podría ser funcional si lo que queremos es insertar fragmentos de HTML inmutables, pero en la mayoría de ocasiones no nos bastará.

### 4 - Crear zonas editables y restringir el contenido.

La siguiente propiedad de `editor.widgets.add()` nos permite definir un objeto en el que **nombrar nuestras diferentes zonas editables y configurarlas a nuestro gusto**. Para nuestro caso, una buena configuración es la siguiente:

```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]',
    }
},
```

Como se puede ver, estamos mapeando elementos HTML como `<h3 class="cta__title">` a entradas como `title`, identificadas por un `selector`. Además, hacemos uso de una **interesantísima funcionalidad** que es la capacidad de **definir el tipo de contenido que cada zona editable permite**.

La propiedad `allowedFormats` utiliza la sintaxis especial de [Allowed Content Rules](https://ckeditor.com/docs/ckeditor4/latest/guide/dev_allowed_content_rules.html) de CKeditor y nos permite restringir el HTML permitido, una funcionalidad muy útil y mucho más granular que los formatos de texto de Drupal.

En este caso, estamos permitiendo en el título el uso de `<strong>` y `<em>`, lo cual suele ser un **requerimiento que da bastantes problemas** en Drupal.

En el cuerpo de texto permitimos el uso de unos pocos elementos más y en el del botón solo permitimos enlaces.

En este momento, **el** ***widget*** **ya debería ser plenamente funcional** y debería permitir al usuario crear, editar e incluso arrastrar y mover cada instancia generada:

En este punto, alguien podría sugerir que el elemento editable `.cta__link` debería ser directamente un `<a>` en lugar de un `<div>`. Es cierto, pero no es posible con la configuración estándar ya que solo los elementos definidos en [CKEDITOR.$dtd.editable](https://ckeditor.com/docs/ckeditor4/latest/api/CKEDITOR_dtd.html#property-S-editable) pueden convertirse en zonas editables.

Es posible cambiar esta configuración para añadir los elementos de tipo `<a>`, pero esto excede el propósito de este tutorial, por lo que lo dejaremos así de momento.

### 5 - El toque final: autodetección y permisos

Tal y como está, el *widget* tiene aún dos posibles problemas.

Por una parte, si estamos usando el filtro "Limita las etiquetas HTML permitidas y corrige el HTML incorrecto", es muy posible que al guardar el contenido o pasar al modo "Fuente HTML" **se nos pierdan las clases CSS y con ellas el diseño.** Esto es debido al sanitizado de CKEditor del que hemos hablado antes.

Si estuviéramos en una configuración estándar de CKEditor, bastaría con añadir la siguiente configuración a `editor.widgets.add()` y ésta se encargaría de añadirlo a la lista blanca de CKeditor:

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

Pero la realidad es que **esto no basta,** ya que Drupal implementa a su manera la configuración de CKEditor y como parece que de momento [no hay un buen modo de añadir nuevas reglas programáticamente](https://www.drupal.org/project/drupal/issues/3052938), tendremos que **añadirlo manualmente en la configuración de cada editor de esta manera:**

Además, ahora mismo la simple inserción del HTML de nuestro call-to-action **no significa que se instancie automáticamente.** Es decir, en el caso de que copie y pegue directamente el HTML de nuestro componente el usuario podría querer editarlo y encontrarse con que no es posible.

Esto se soluciona rápidamente implementando el método `upcast()`, el cual no es obligatorio, pero sí muy recomendable:

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

Cada vez que procesa el HTML, CKEditor itera sobre el HTML elemento por elemento y nos da una oportunidad de indicar que queremos inicializar nuestro *widget*. En este caso, estamos indicando a CKEditor que **intente construir el widget** si encuentra un `<div>` con la clase `.cta`. El editor comprobará si este elemento coincide con nuestro *template* y, de ser así, lo inicializará.

Nuestro código final quedaría así:

```javascript
CKEDITOR.plugins.add( 'cta_widget', {
    // Dependencia del plugin "widget"
    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]',
                }
            },
            
            // No es necesario, se configura en Drupal, pero es bueno mantenerlo por si cambia en el futuro.
            allowedContent:
                'h3(cta__title); div(cta__text, cta__link)',
            
            // No es obligatorio pero permite reconocer widgets en copy & paste.
            upcast: function( element ) {
                return element.name == 'section' && element.hasClass( 'cta__widget' );
            },
        });

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


```

### ¿Cuándo y por qué es mejor un *widget* que un paragraph?

La idea principal de este artículo es que **los widgets de CKeditor** ***pueden*** **sustituir en algunos casos a paragraphs y fields**. Esto puede sonar mal de entrada: ¿por qué prescindir de las capacidades de Drupal 8 para delegarlas en un editor de texto? ¿No existen precisamente los campos o los paragraphs para *evitar* la tentación de solucionarlo todo en el editor? ¿No introducirá más problemas de mantenimiento a largo plazo de los que elimina en el corto?

Hay que insistir en que solo deberías considerar esta opción en **algunos casos** muy controlados en los que se cumplan los siguientes requisitos:

1. La naturaleza del componente a insertar es de **totalmente estética o visual**, es decir, no depende ni interactúa *de ninguna manera* con Drupal. 
    - ¿Necesita control de acceso, hacer una referencia a una entidad, acceder a las traducciones, etc.? Olvídate de los *widgets*.
    - ¿Es una manera bonita o diferente de presentar una información que podría perfectamente mostrarse sin estilado alguno? **¿Es puro** ***front-end*****?** Entonces es un buen candidato.
2. **No se prevén grandes cambios en su estructura HTML,** al menos en el medio plazo: puede haber cambios de estilado, pero el HTML permanecerá intacto.

Si un componente cumple ambos requisitos, es **muy recomendable plantearlo como** ***widget***, ya que introduce varias mejoras sobre paragraphs o fields:

- **Evita la sobrecarga** de nuestros tipos de contenido con campos y paragraphs innecesarios.
- Proporciona al usuario editor una **verdadera experiencia WYSIWYG** en la que poder comprobar en tiempo real cuál es el resultado de su trabajo. Esto, además, suele ser una de las principales quejas que los editores tienen sobre Drupal.
- Permite un **marcado más sencillo e inmediato.** Para estilar el HTML y CSS de un paragraph en ocasiones es necesario intervenir a varios niveles, usando varios *templates,* *hooks o* *field fomatters*. Con un *widget* de CKeditor, el marcado se escribe directamente en un template. En un cierto sentido (repito: *en un cierto sentido*), se acerca más al paradigma del diseño guiado por componentes (Component-Driven Design) de Vue, React o Angular.
- Gracias al sistema de Allowed Content Rules de CKEditor, permite **definir con granularidad el HTML permitido y prohibido** en cada zona editable de un modo más detallado e individualizado de lo que permitirían los campos de Drupal. Ello nos permite darle más poder al editor sin introducir el riesgo de HTML malformado o erróneo.
- No nos ha dado tiempo a profundizar en ello, pero **es posible exponer opciones de configuración del widget** usando la API de *dialog*. De este modo, se podrían exponer opciones como añadir margin o padding, eliminar ciertas zonas editables, cambiar el color de fondo, etcétera.

En resumen, los *widgets* de CKEditor son una alternativa para algunos tipos de componente que solemos solucionar con herramientas de Drupal. En el caso de componentes puramente visuales, pequeños y con requisitos estables, los *widgets* aportan muchas ventajas de cara al usuario final y son mucho más fáciles de desarrollar y mantener.



[Paragraphs (módulo de Drupal)](https://metadrop.net/es/articulos?text=Paragraphs%20%28m%C3%B3dulo%20de%20Drupal%29)

[CKEditor](https://metadrop.net/es/articulos?text=CKEditor)

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

[HTML](https://metadrop.net/es/articulos?text=HTML)

[JavaScript](https://metadrop.net/es/articulos?text=JavaScript)

 

[Desarrollo de módulos e integraciones de terceros con Drupal](https://metadrop.net/es/servicios/drupal/integraciones " See Desarrollo de módulos e integraciones de terceros con Drupal")

Metadrop conecta Drupal con tu CRM, ERP, pasarela de pago y stack de marketing — Salesforce, Microsoft Dynamics, TPV virtual de BBVA, Workday y Keepeek — mediante módulos personalizados y APIs seguras y conformes con el RGPD.

 

 Ver más