---
url: 'https://dxpr.com/fr/hc/documentation/learn-dxpr-builder/development/custom-elements'
title: 'Créer des éléments personnalisés | DXPR'
author:
  name: Jurriaan
  url: 'https://dxpr.com/users/jurriaan'
date: '2026-10-24T11:00:47+00:00'
copyright: 'Copyright DXPR. All rights reserved.'
type: article
summary: 'Chaque élément DXPR Builder est un objet JavaScript empilé sur window.dxprBuilder.dxpr_elements. L’éditeur transforme chaque objet en classe d’élément avec les onglets Style et Animation déjà attachés. Cette page montre comment les éléments livrés sont définis dans dxpr_builder/dxpr_elements.js, comment un module ajoute sa propre définition à l’éditeur, et à quoi ressemble le HTML résultant. Il n’y a pas de modèles Twig côté serveur par élément ; les éléments sont rendus dans le navigateur.'
tags:
  - 'Bouw erop voort'
image: 'https://dxpr.com/themes/custom/dxpr_com/images/dxpr-share-card.jpg'
published: true
og:
  site_name: DXPR
  updated_time: '2026-10-24T11:00:47+0000'
  'image:alt': 'DXPR, powered by Drupal: AI-powered Drupal for marketers.'
schema:
  '@context': 'https://schema.org'
  '@graph':
    -
      mainEntity: {  }
    -
      '@type': TechArticle
      headline: 'Créer des éléments personnalisés'
      description: 'Chaque élément DXPR Builder est un objet JavaScript empilé sur window.dxprBuilder.dxpr_elements. L’éditeur transforme chaque objet en classe d’élément avec les onglets Style et Animation déjà attachés. Cette page montre comment les éléments livrés sont définis dans dxpr_builder/dxpr_elements.js, comment un module ajoute sa propre définition à l’éditeur, et à quoi ressemble le HTML résultant. Il n’y a pas de modèles Twig côté serveur par élément ; les éléments sont rendus dans le navigateur.'
      image:
        '@type': ImageObject
        url: 'https://dxpr.com/themes/custom/dxpr_com/images/dxpr-share-card.jpg'
        width: '1200'
        height: '630'
      datePublished: '2026-10-24T11:00:47+0000'
      dateModified: '2026-10-24T11:00:47+0000'
      author:
        '@type': Organization
        '@id': 'https://dxpr.com/#organization'
        name: DXPR
        url: 'https://dxpr.com/'
        logo:
          '@type': ImageObject
          url: 'https://dxpr.com/themes/custom/dxpr_com/dxpr-logo-dark.svg'
          width: '1348'
          height: '501'
      publisher:
        '@type': Organization
        '@id': 'https://dxpr.com/#organization'
        name: DXPR
        url: 'https://dxpr.com/'
        logo:
          '@type': ImageObject
          url: 'https://dxpr.com/themes/custom/dxpr_com/dxpr-logo-dark.svg'
          width: '1348'
          height: '501'
      mainEntityOfPage: 'https://dxpr.com/fr/hc/documentation/learn-dxpr-builder/development/custom-elements'
      articleSection: 'Bouw erop voort'
    -
      '@type': WebPage
      '@id': 'https://dxpr.com/fr/hc/documentation/learn-dxpr-builder/development/custom-elements'
      description: 'Chaque élément DXPR Builder est un objet JavaScript empilé sur window.dxprBuilder.dxpr_elements. L’éditeur transforme chaque objet en classe d’élément avec les onglets Style et Animation déjà attachés. Cette page montre comment les éléments livrés sont définis dans dxpr_builder/dxpr_elements.js, comment un module ajoute sa propre définition à l’éditeur, et à quoi ressemble le HTML résultant. Il n’y a pas de modèles Twig côté serveur par élément ; les éléments sont rendus dans le navigateur.'
      breadcrumb:
        '@type': BreadcrumbList
        itemListElement:
          -
            '@type': ListItem
            position: 1
            name: Accueil
            item: 'https://dxpr.com/fr'
      publisher:
        '@type': Organization
        '@id': 'https://dxpr.com/#organization'
        name: DXPR
      inLanguage: fr
---
[Help Center](/fr/hc)/[Documentation](/fr/hc/documentation) /[Learn DXPR Builder](/hc/documentation/learn-dxpr-builder) /[Extend](/hc/documentation/learn-dxpr-builder/extend) /[Bouw erop voort](/hc/documentation/learn-dxpr-builder/extend/build-on-it) # Créer des éléments personnalisés.

 Learn DXPR BuilderExtendBouw erop voort

   ## In this guide

 Related guides

- [Référence de l’API des hooks](/fr/hc/documentation/learn-dxpr-builder/development/hooks-api)
- [Architecture](/fr/hc/documentation/learn-dxpr-builder/development/architecture)

Chaque élément DXPR Builder est un objet JavaScript empilé sur `window.dxprBuilder.dxpr_elements`. L’éditeur transforme chaque objet en classe d’élément avec les onglets Style et Animation déjà attachés. Cette page montre comment les éléments livrés sont définis dans `dxpr_builder/dxpr_elements.js`, comment un module ajoute sa propre définition à l’éditeur, et à quoi ressemble le HTML résultant. Il n’y a pas de modèles Twig côté serveur par élément ; les éléments sont rendus dans le navigateur.

## Comment l’éditeur enregistre les éléments

L’éditeur charge six scripts dans cet ordre, définis dans `dxpr_builder_library_info_build()` dans `dxpr_builder.module` comme la bibliothèque `dxpr_builder/editor.builder` :

1. `dxpr_templates.js` : modèles twig.js côté client compilés depuis `dxpr_builder/templates/`.
2. `dxpr_global.js` : utilitaires partagés et l’intégration CKEditor 5.
3. `dxpr_events.js`
4. `dxpr_param_types.js` : empile un objet par type d’entrée sur `window.dxprBuilder.dxpr_param_types`.
5. `dxpr_elements.js` : crée le tableau `window.dxprBuilder.dxpr_elements` et empile une définition par élément.
6. `dxpr_builder.js` : l’éditeur. Quand ce fichier s’exécute, il appelle `create_dxpr_elements()`, qui boucle sur `window.dxprBuilder.dxpr_elements` et pour chaque définition :
    - appelle `register_animated_element(base, is_container, Class)`, pour que la classe étende `AnimatedElement` (onglet Style, onglet Animation, HTML ID, HTML classes et paramètres de placement) ;
    - ajoute les paramètres hérités aux `params` de la définition ;
    - convertit chaque paramètre via `make_param_type()`, qui fait correspondre `param.type` aux types de paramètres enregistrés ;
    - mélange les propriétés de la définition dans le prototype de la classe.

Une définition doit donc être sur le tableau après l’exécution de `dxpr_elements.js` et avant celle de `dxpr_builder.js`. Les six fichiers se chargent avec l’attribut `defer`, donc l’ordre des balises de script dans le document est l’ordre d’exécution.

## La définition d’élément

Voici l’élément Alert de `dxpr_builder/dxpr_elements.js` (source : `dxpr_builder/build/dxpr-elements/elements/az_alert.js`), réduit aux propriétés requises par chaque élément. L’original définit aussi `el_class`, un gestionnaire `onCancel` et un écouteur `focusout`.

```javascript
window.dxprBuilder.dxpr_elements.push({
  base: "az_alert",
  name: Drupal.t("Alert"),
  nameEn: "Alert",
  icon: "az-custom-icon az-alert-icon",
  largeSettingsWindow: true,
  hidden: false,
  params: [
    {
      type: "textarea",
      heading: Drupal.t("Message"),
      param_name: "content",
      value: Drupal.t(
        "This is a placeholder text. Your actual content will go here. Edit this to include your own information.",
      ),
    },
    {
      type: "dropdown",
      heading: Drupal.t("Type"),
      param_name: "type",
      description: Drupal.t("Select message type."),
      getText: (opt) => opt.text,
      getValue: (opt) => opt.value,
      getDefault: (param) => param.value["alert-success"].value,
      value: {
        "alert-success": { text: Drupal.t("Success"), value: "alert-success" },
        "alert-info": { text: Drupal.t("Info"), value: "alert-info" },
        "alert-warning": { text: Drupal.t("Warning"), value: "alert-warning" },
        "alert-danger": { text: Drupal.t("Danger"), value: "alert-danger" },
      },
      passthrough: true,
    },
  ],
  show_settings_on_create: true,
  has_content: true,
  render(...args) {
    const state = {
      class: `az-element az-alert alert ${this.attrs.type} ${this.get_el_classes()} ${this.get_content_classes()}`,
      style: this.attrs.style,
      message: this.attrs.content,
    };
    this.dom_element = this.baseclass.prototype.render_template(
      "elements/alert/alert",
      state,
    );
    this.baseclass.prototype.render.apply(this, args);
  },
  save_history() {
    const container = this.get_my_container();
    container.save_container_history(Drupal.t("Edit alert"));
  },
});

```

| Propriété | Rôle |
|---|---|
| `base` | Nom machine. Il est écrit dans le HTML enregistré comme `data-azb="az_alert"` et sert à retrouver la classe quand la page est de nouveau analysée. Gardez le préfixe `az_`. |
| `name`, `nameEn` | Libellé dans le sélecteur d’éléments et sa forme non traduite. |
| `icon` | Classes CSS pour l’icône du sélecteur. Les icônes livrées sont définies dans `dxpr_builder/sass/dxpr-builder-backend/components/_custom-icons.scss` ; un élément personnalisé a besoin de son propre CSS pour sa classe d’icône. |
| `category` | Onglet du sélecteur. Les éléments de mise en page utilisent `Drupal.t("Layout")` ; blocs et vues utilisent `"CMS"`. Omettez-le pour l’onglet de contenu par défaut. |
| `is_container` | `true` quand l’élément contient d’autres éléments. |
| `largeSettingsWindow` | Ouvre le dialogue de réglages en grande taille. |
| `hidden` | Masque l’élément du sélecteur tout en le gardant rendable. |
| `params` | Réglages montrés dans le dialogue. Voir ci-dessous. |
| `show_settings_on_create` | Ouvre le dialogue de réglages dès que l’élément est déposé. |
| `has_content` | L’élément a une zone de contenu modifiable (`dom_content_element`). |
| `render(...args)` | Construit `this.dom_element` depuis `this.attrs`, puis appelle le `render` de base. |
| `save_history()` | Empile une entrée d’annulation avec un libellé. |
| `parse_attrs_from_dom(element)` | Optionnel. Relit les valeurs de paramètres depuis le balisage enregistré, utilisé avec `skip_data_azb` et `skip_params` par des éléments comme Image qui stockent les valeurs dans les attributs `<img>` au lieu de `data-azat-*`. |

### Paramètres

Chaque entrée de `params` a :

| Clé | Sens |
|---|---|
| `type` | L’un des types de paramètres enregistrés (section suivante). |
| `heading` | Libellé du champ. |
| `param_name` | Clé dans `this.attrs` et, dans le HTML enregistré, l’attribut `data-azat-<param_name>`. `content` est spécial : c’est le corps de l’élément, pas un attribut. |
| `value` | Valeur par défaut. Pour `dropdown` c’est un objet d’options avec les callbacks `getText`, `getValue` et `getDefault`. |
| `description` | Texte d’aide sous le champ. |
| `tab` | Regroupe le champ dans un accordéon nommé, par exemple `Drupal.t("SEO")`. |
| `can_be_empty` | Stocke une chaîne vide au lieu de retirer l’attribut. |
| `safe` | Ignore l’encodage URL de la valeur stockée. |

Les types de paramètres sont les valeurs `type` empilées sur `window.dxprBuilder.dxpr_param_types` dans `dxpr_builder/dxpr_param_types.js` : `boxmodel`, `checkbox`, `checkboxes`, `colorpicker`, `datetime`, `dropdown`, `dxb_slider`, `html`, `icon`, `image`, `images`, `link`, `links`, `numericfield`, `row_layout`, `saved_datetime`, `social_links`, `textarea`, `textfield` et `video`. Un type non enregistré retombe sur un champ de texte brut (`BaseParamType`).

Ne réutilisez pas un `param_name` hérité par chaque élément, comme `color`, `background`, `el_class`, `hash` ou les noms d’animation `an_*`. L’onglet Style possède déjà `color`, donc un second paramètre avec ce nom n’est jamais écrit dans `data-azat-color`.

### Ce qui est enregistré

`update_data()` dans `dxpr_builder.js` écrit l’élément en HTML :

- `data-azb="<base>"` sur l’élément racine ;
- `data-azat-<param_name>="<value>"` pour chaque paramètre dont la valeur diffère de son défaut, encodé en URL sauf si le paramètre définit `safe: true` ;
- les paramètres de l’onglet Style comme `style` en ligne et classes, jamais comme attributs `data-azat-*` ;
- `data-azcnt="true"` sur l’élément de contenu quand `has_content` est défini.

Les visiteurs sans éditeur reçoivent ce HTML sans ses attributs `data-az*` : `DxprBuilderService::parseDocumentForCleanup()` les retire, sauf une liste fixe conservée sur les éléments animés et sur les éléments dynamiques intégrés comme Carousel. Si l’élément a besoin de JavaScript ou de CSS pour les visiteurs, posez un attribut `data-dxpr-builder-libraries="<key>"` sur sa racine : `parseDocumentForTemplateLibrary()` attache `dxpr_builder/elements.<key>` pour chaque clé sans barre oblique. Les clés sont les bibliothèques `elements.*` dans `dxpr_builder.libraries.yml` ; une clé avec une barre oblique n’est honorée que si elle est la `library` d’un jeu d’icônes enregistré.

## Exemple minimal fonctionnel

L’exemple ajoute un élément « Notice » qui rend un paragraphe avec une couleur choisie. Il construit son DOM directement car les modèles twig.js compilés dans `dxpr_templates.js` font partie du paquet de l’éditeur et ne peuvent pas être étendus par un autre module.

1. Créez `mymodule/js/notice-element.js` :

    ```javascript
    window.dxprBuilder.dxpr_elements.push({
      base: "az_notice",
      name: Drupal.t("Notice"),
      nameEn: "Notice",
      icon: "az-custom-icon az-alert-icon",
      params: [
        {
          type: "textfield",
          heading: Drupal.t("Text"),
          param_name: "text",
          value: Drupal.t("Notice text"),
        },
        {
          type: "colorpicker",
          heading: Drupal.t("Colour"),
          param_name: "notice_color",
          value: "#0d6efd",
        },
      ],
      show_settings_on_create: true,
      render(...args) {
        const el = document.createElement("div");
        el.className = `az-element az-notice ${this.get_el_classes()}`;
        if (this.attrs.style) {
          el.setAttribute("style", this.attrs.style);
        }
        const p = document.createElement("p");
        p.style.color = this.attrs.notice_color;
        p.textContent = this.attrs.text;
        el.appendChild(p);
        this.dom_element = el;
        this.baseclass.prototype.render.apply(this, args);
      },
      save_history() {
        this.get_my_container().save_container_history(Drupal.t("Edit notice"));
      },
    });

    ```
2. Insérez le fichier dans la bibliothèque de l’éditeur entre `dxpr_elements.js` (poids `-4`) et `dxpr_builder.js` (poids `-3`) dans `mymodule.module` :

    ```php
    /**
     * Implements hook_library_info_alter().
     */
    function mymodule_library_info_alter(array &$libraries, string $extension): void {
      if ($extension === 'dxpr_builder' && isset($libraries['editor.builder'])) {
        $path = '/' . \Drupal::service('extension.list.module')->getPath('mymodule');
        $libraries['editor.builder']['js'][$path . '/js/notice-element.js'] = [
          'weight' => -3.5,
          'preprocess' => FALSE,
          'attributes' => ['defer' => TRUE],
        ];
      }
    }

    ```

    `preprocess: FALSE` et `defer` correspondent aux entrées que le module définit pour ses propres fichiers, pour que le fichier garde sa place dans la séquence, que les ressources de l’éditeur viennent du CDN ou d’un build local.
3. Videz le cache et ouvrez une page du builder :

    ```bash
    drush cr

    ```
4. Ajoutez l’élément depuis le sélecteur, définissez son texte et sa couleur, et enregistrez. Le champ stocke :

    ```html
    <div class="az-element az-notice" id="..." data-azb="az_notice"
         data-azat-text="Hello" data-azat-notice_color="%23198754">
      <p style="color: #198754;">Hello</p>
    </div>

    ```

    Un visiteur reçoit le même balisage sans les attributs `data-az*`. Le paragraphe garde sa couleur en ligne, donc aucune bibliothèque visiteur n’est requise pour cet élément.

## Limites à connaître

- **Les profils ne listent pas les éléments personnalisés.** La liste d’éléments d’un profil du builder vient d’une liste fixe dans `src/Form/DxprBuilderProfileForm.php` et `src/Service/Handler/ProfileHandler.php`. Les éléments hors de cette liste ne sont jamais masqués par un profil.
- **Les icônes pour le sélecteur** ont besoin de CSS depuis votre module ; les classes `az-custom-icon` livrées par le builder ne couvrent que les éléments livrés.
- **`DxprBuilderService::getDxprElementsFolders()`** et son `hook_dxpr_builder_elements_folders_alter()` renvoient une liste de dossiers `elements` dans les thèmes, mais rien dans l’éditeur ne lit cette liste. Ce n’est pas une voie d’enregistrement.
- **Les onglets Style et Animation** sont ajoutés pour vous par `register_animated_element()` ; ne redéfinissez pas ces paramètres.

## Et ensuite ?

- [API des hooks](/hc/documentation/learn-dxpr-builder/development/hooks-api) pour les hooks PHP qui ajoutent des classes utilitaires et des jeux d’icônes.
- [Architecture](/hc/documentation/learn-dxpr-builder/development/architecture) pour l’articulation entre l’éditeur et le formateur de champ.