Cómo crear módulos personalizados en HubSpot: Guía completa (2026)

The Rapid Team

Si trabajas con el CMS de HubSpot, los módulos personalizados son la habilidad más importante que puedes aprender. Otorgan a los editores de contenido un control total sobre el contenido de la página mientras mantienen el código limpio, rápido y fácil de mantener.

Pero aquí está el problema — la mayoría de los desarrolladores de HubSpot construyen los módulos de la manera equivocada. Escriben CSS sobrecargado, ignoran la agrupación de campos, codifican de forma rígida (hardcode) las etiquetas de encabezado e ignoran por completo el rendimiento. ¿El resultado? Páginas lentas, editores frustrados y un código desordenado que nadie quiere mantener.

Esta guía cubre todo lo que necesitas para construir módulos personalizados listos para producción en HubSpot — de la manera correcta. Con ejemplos de código reales, una estructura de campos adecuada, mejores prácticas de HubL y consejos de rendimiento que realmente importan.

Entendiendo los Módulos Personalizados de HubSpot

Un módulo personalizado es un bloque de contenido reutilizable compuesto por cuatro archivos dentro del Administrador de Diseño (Design Manager):

  • module.html — Tu plantilla HTML con etiquetas HubL que renderizan contenido dinámico
  • fields.json — Define qué campos ven y con los que interactúan los editores de contenido
  • module.css — Estilos opcionales limitados a este módulo
  • meta.json — Configuración como etiqueta, icono y qué tipos de plantillas pueden usar este módulo

Piensa en ello como construir un bloque de Lego. Lo diseñas una vez con todos los puntos de conexión correctos, y luego los editores de contenido pueden usarlo en cualquier lugar — en cualquier página, en cualquier sección — sin tocar nunca el código.

Configurando meta.json de la Manera Correcta

Antes de construir cualquier cosa, configura tu módulo adecuadamente. Aquí hay un meta.json limpio para un módulo de Sección Hero:

{% module_block module "widget_7d7b35d9-a56e-43e4-be6c-d5e41925e11d" %}{% module_attribute "child_css" is_json="true" %}null{% end_module_attribute %}{% module_attribute "code_block" is_json="true" %}"{
  \"label\": \"Sección Hero\",
  \"css_assets\": [],
  \"external_js\": [],
  \"global\": false,
  \"help_text\": \"Una sección hero flexible con título, descripción, imagen y botones de llamada a la acción (CTA).\",
  \"host_template_types\": [\"PAGE\", \"BLOG_LISTING\", \"BLOG_POST\"],
  \"icon\": \"\",
  \"is_available_for_new_content\": true,
  \"js_assets\": [],
  \"other_assets\": [],
  \"smart_type\": \"NOT_SMART\",
  \"categories\": [\"DESIGN\"]
}"{% end_module_attribute %}{% module_attribute "css" is_json="true" %}null{% end_module_attribute %}{% module_attribute "label" is_json="true" %}null{% end_module_attribute %}{% module_attribute "module_id" is_json="true" %}321957934793{% end_module_attribute %}{% module_attribute "schema_version" is_json="true" %}2{% end_module_attribute %}{% module_attribute "tag" is_json="true" %}"module"{% end_module_attribute %}{% end_module_block %}

Dos cosas a tener en cuenta aquí. Primero, host_template_types controla dónde se puede usar este módulo. Establécelo en PAGE para módulos exclusivos de páginas, o incluye BLOG_POST si también debe funcionar en plantillas de blog. Nunca lo dejes abierto para plantillas de correo electrónico si tu módulo usa CSS o JS — los correos electrónicos no los soportan. Segundo, añade siempre un help_text (texto de ayuda) significativo. Esto aparece en el Administrador de Diseño y ayuda a tu equipo a entender qué hace el módulo sin necesidad de leer el código.

Construyendo una Estructura fields.json Adecuada

El archivo fields.json es donde la mayoría de los desarrolladores se equivocan. Una lista plana de más de 15 campos hace que la experiencia de edición sea dolorosa. Agrupar campos relacionados crea una interfaz limpia e intuitiva.

Configuración de la Sección — Todo Módulo Necesita Esto

Antes de cualquier campo de contenido, cada módulo debe tener un grupo de Configuración de la Sección. Esto da a los editores de contenido control sobre el ID de la sección, las clases personalizadas y la visibilidad:

{% module_block module "widget_9fd812aa-92d5-44cb-9919-4c387c104e94" %}{% module_attribute "child_css" is_json="true" %}null{% end_module_attribute %}{% module_attribute "code_block" is_json="true" %}"{
  \"id\": \"section_settings\",
  \"name\": \"section_settings\",
  \"label\": \"Configuración de la Sección\",
  \"type\": \"group\",
  \"children\": [
    {
      \"id\": \"section_id\",
      \"name\": \"section_id\",
      \"label\": \"ID de la Sección\",
      \"type\": \"text\",
      \"help_text\": \"Añade un ID único para enlaces ancla. Ejemplo: seccion-hero\",
      \"default\": \"\"
    },
    {
      \"id\": \"section_class\",
      \"name\": \"section_class\",
      \"label\": \"Clase CSS Personalizada\",
      \"type\": \"text\",
      \"help_text\": \"Añade clases de Tailwind o CSS personalizadas al contenedor de la sección.\",
      \"default\": \"\"
    },
    {
      \"id\": \"section_visible\",
      \"name\": \"section_visible\",
      \"label\": \"Mostrar Sección\",
      \"type\": \"boolean\",
      \"help_text\": \"Desactiva para ocultar esta sección sin eliminar su contenido.\",
      \"default\": true
    }
  ]
}"{% end_module_attribute %}{% module_attribute "css" is_json="true" %}null{% end_module_attribute %}{% module_attribute "label" is_json="true" %}null{% end_module_attribute %}{% module_attribute "module_id" is_json="true" %}321957934793{% end_module_attribute %}{% module_attribute "schema_version" is_json="true" %}2{% end_module_attribute %}{% module_attribute "tag" is_json="true" %}"module"{% end_module_attribute %}{% end_module_block %}

¿Por qué es importante esto? Los editores de contenido a menudo necesitan ocultar una sección temporalmente, agregar un enlace ancla para la navegación o aplicar una clase personalizada para un estilo especial. Sin estos campos, tienen que pedir a un desarrollador cada pequeño cambio. Con ellos, son autosuficientes.

Grupo de Contenido — Encabezados con Control de SEO

Nunca codifiques de forma rígida (hardcode) tu etiqueta de encabezado. Proporciona siempre un campo de opción para que los editores puedan controlar la jerarquía de SEO:

{% module_block module "widget_923b9d64-2d51-44cf-b6b0-d50928e48934" %}{% module_attribute "child_css" is_json="true" %}null{% end_module_attribute %}{% module_attribute "code_block" is_json="true" %}"{
  \"id\": \"content\",
  \"name\": \"content\",
  \"label\": \"Contenido\",
  \"type\": \"group\",
  \"children\": [
    {
      \"id\": \"heading\",
      \"name\": \"heading\",
      \"label\": \"Título\",
      \"type\": \"text\",
      \"help_text\": \"Título principal. Mantenlo bajo 60 caracteres para obtener los mejores resultados de SEO.\",
      \"default\": \"Construye Algo Increíble\"
    },
    {
      \"id\": \"heading_tag\",
      \"name\": \"heading_tag\",
      \"label\": \"Etiqueta del Título\",
      \"type\": \"choice\",
      \"help_text\": \"Usa H1 solo una vez por página. Usa H2 para todos los demás títulos de sección.\",
      \"choices\": [
        [\"h1\", \"H1 — Título Principal de la Página\"],
        [\"h2\", \"H2 — Título de la Sección\"],
        [\"h3\", \"H3 — Título de la Subsección\"]
      ],
      \"default\": \"h2\"
    },
    {
      \"id\": \"description\",
      \"name\": \"description\",
      \"label\": \"Descripción\",
      \"type\": \"textarea\",
      \"help_text\": \"Texto de apoyo debajo del título. Se recomiendan 1-2 oraciones.\",
      \"default\": \"\"
    }
  ]
}"{% end_module_attribute %}{% module_attribute "css" is_json="true" %}null{% end_module_attribute %}{% module_attribute "label" is_json="true" %}null{% end_module_attribute %}{% module_attribute "module_id" is_json="true" %}321957934793{% end_module_attribute %}{% module_attribute "schema_version" is_json="true" %}2{% end_module_attribute %}{% module_attribute "tag" is_json="true" %}"module"{% end_module_attribute %}{% end_module_block %}

Nota que la etiqueta de título predeterminada está establecida en h2, no en h1. Esto es intencional. La mayoría de las páginas solo deberían tener un H1, y eso generalmente lo maneja el primer módulo. Cada módulo posterior debe tener el estándar H2. Si el editor coloca este módulo como la primera sección de una página, él mismo puede cambiarlo a H1.

Grupo de Botones — Usa Campos de Enlace, No CTAs

Un error común es usar el módulo CTA de HubSpot para cada botón. Los CTAs son poderosos, pero añaden sobrecarga de seguimiento y ralentizan la carga de la página cuando se usan en exceso. Para la mayoría de los botones, un simple campo de enlace con un campo de texto es todo lo que necesitas:

{% module_block module "widget_c54b4974-9628-46f5-8cb4-95f80525fce0" %}{% module_attribute "child_css" is_json="true" %}null{% end_module_attribute %}{% module_attribute "code_block" is_json="true" %}"{
  \"id\": \"primary_button\",
  \"name\": \"primary_button\",
  \"label\": \"Botón Principal\",
  \"type\": \"group\",
  \"children\": [
    {
      \"id\": \"button_text\",
      \"name\": \"button_text\",
      \"label\": \"Texto del Botón\",
      \"type\": \"text\",
      \"default\": \"Comenzar\"
    },
    {
      \"id\": \"button_link\",
      \"name\": \"button_link\",
      \"label\": \"Enlace del Botón\",
      \"type\": \"link\",
      \"default\": {
        \"url\": { \"href\": \"#\", \"type\": \"EXTERNAL\" },
        \"open_in_new_tab\": false,
        \"no_follow\": false
      }
    }
  ]
}"{% end_module_attribute %}{% module_attribute "css" is_json="true" %}null{% end_module_attribute %}{% module_attribute "label" is_json="true" %}null{% end_module_attribute %}{% module_attribute "module_id" is_json="true" %}321957934793{% end_module_attribute %}{% module_attribute "schema_version" is_json="true" %}2{% end_module_attribute %}{% module_attribute "tag" is_json="true" %}"module"{% end_module_attribute %}{% end_module_block %}

El tipo de campo de enlace da automáticamente a los editores opciones de URL, nueva pestaña y nofollow — todo sin ningún trabajo extra por tu parte.

Convenciones de Nomenclatura de Campos

Cómo nombras los campos importa más de lo que crees. Los editores de contenido ven estas etiquetas cada vez que editan una página. Sé específico:

  • ❌ "Imagen" — ¿qué imagen? ¿Fondo? ¿Perfil? ¿Hero?
  • ✅ "Imagen de Fondo del Hero" — claro al instante
  • ❌ "Alternar" (Toggle) — ¿alternar qué?
  • ✅ "Mostrar Sección" o "Activar Superposición de Fondo" — describe exactamente lo que controla
  • ❌ "Texto" — cada campo es texto
  • ✅ "Texto del Botón" o "Subtítulo" — específico y obvio

Añade también help_text (texto de ayuda) a cada campo. Una descripción de 10 palabras ahora te ahorra una conversación de 10 minutos en Slack más tarde.

Escribiendo el HTML del Módulo + HubL

Ahora vamos a construir el module.html real. Aquí es donde todos los campos se unen en un componente renderizado.

El module.html Completo de la Sección Hero

{% module_block module "widget_a6588a27-cbbf-4255-abfb-c0cbf6ad937e" %}{% module_attribute "child_css" is_json="true" %}null{% end_module_attribute %}{% module_attribute "code_block" is_json="true" %}"{%- if module.section_settings.section_visible -%}
<section
  {%- if module.section_settings.section_id %} id=\"{{ module.section_settings.section_id }}\"{% endif -%}
  class=\"relative overflow-hidden {{ module.section_settings.section_class }}\"
>
  <div class=\"max-w-7xl mx-auto px-4 sm:px-6 lg:px-8 py-16 sm:py-20 lg:py-24\">
    <div class=\"grid grid-cols-1 lg:grid-cols-2 gap-12 items-center\">

      <div class=\"flex flex-col gap-6\">

        {%- if module.content.heading -%}
        <{{ module.content.heading_tag }} class=\"text-4xl sm:text-5xl lg:text-6xl font-bold tracking-tight text-slate-900\">
          {{- module.content.heading -}}
        </{{ module.content.heading_tag }}>
        {%- endif -%}

        {%- if module.content.description -%}
        <p class=\"text-lg text-slate-600 leading-relaxed max-w-xl\">
          {{- module.content.description -}}
        </p>
        {%- endif -%}

        {%- if module.primary_button.button_text -%}
        <div class=\"flex flex-wrap gap-4 mt-2\">
          <a
            href=\"{{ module.primary_button.button_link.url.href }}\"
            {%- if module.primary_button.button_link.open_in_new_tab %} target=\"_blank\" rel=\"noopener noreferrer\"{% endif -%}
            class=\"inline-flex items-center px-8 py-4 text-base font-semibold text-white bg-blue-600 rounded-xl hover:bg-blue-700 transition-colors\"
          >
            {{- module.primary_button.button_text -}}
          </a>
        </div>
        {%- endif -%}

      </div>

      {%- if module.hero_image.image.src -%}
      <div class=\"relative\">
        <img
          src=\"{{ module.hero_image.image.src }}\"
          alt=\"{{ module.hero_image.image.alt }}\"
          width=\"{{ module.hero_image.image.width }}\"
          height=\"{{ module.hero_image.image.height }}\"
          loading=\"lazy\"
          class=\"w-full h-auto rounded-2xl\"
        >
      </div>
      {%- endif -%}

    </div>
  </div>
</section>
{%- endif -%}"{% end_module_attribute %}{% module_attribute "css" is_json="true" %}null{% end_module_attribute %}{% module_attribute "label" is_json="true" %}null{% end_module_attribute %}{% module_attribute "module_id" is_json="true" %}321957934793{% end_module_attribute %}{% module_attribute "schema_version" is_json="true" %}2{% end_module_attribute %}{% module_attribute "tag" is_json="true" %}"module"{% end_module_attribute %}{% end_module_block %}

Vamos a desglosar los patrones críticos utilizados en este código.

Control de Espacios en Blanco con {%- -%}

Cada etiqueta HubL en el código anterior usa la sintaxis de guion: {%- -%} en lugar de {% %}. Este único cambio marca una diferencia enorme en tu HTML renderizado.

Sin los guiones, HubL inserta líneas en blanco dondequiera que procese una etiqueta. El código fuente de tu página termina lleno de espacios en blanco innecesarios — líneas vacías entre cada elemento. Esto hace que el HTML sea más pesado, más difícil de depurar y ligeramente más lento de analizar.

Con guiones, HubL elimina esos espacios en blanco limpiamente. El HTML renderizado es ajustado, mínimo y exactamente lo que necesita el navegador. En una página con más de 20 módulos, esto puede reducir notablemente el tamaño de tu archivo HTML.

Lo mismo se aplica a la salida de variables: usa {{- -}} en lugar de {{ }} para evitar espacios en blanco alrededor de los valores dinámicos.

Etiquetas de Encabezado Dinámicas

El título usa el valor del campo directamente como la etiqueta HTML:

{% module_block module "widget_f4480bd4-b61f-4211-b622-ce6173bef361" %}{% module_attribute "child_css" is_json="true" %}null{% end_module_attribute %}{% module_attribute "code_block" is_json="true" %}"<{{ module.content.heading_tag }}>
  {{- module.content.heading -}}
</{{ module.content.heading_tag }}>"{% end_module_attribute %}{% module_attribute "css" is_json="true" %}null{% end_module_attribute %}{% module_attribute "label" is_json="true" %}null{% end_module_attribute %}{% module_attribute "module_id" is_json="true" %}321957934793{% end_module_attribute %}{% module_attribute "schema_version" is_json="true" %}2{% end_module_attribute %}{% module_attribute "tag" is_json="true" %}"module"{% end_module_attribute %}{% end_module_block %}

Si el editor selecciona "H2" en el menú desplegable, esto se renderiza como una etiqueta <h2> adecuada. Si selecciona "H1", se renderiza como <h1>. El editor tiene control total del SEO sin necesitar a un desarrollador.

Renderizado Condicional

Cada sección de contenido está envuelta en una comprobación if. Si el campo de título está vacío, no se renderiza ninguna etiqueta <h2>. Si el texto del botón está vacío, no aparece ningún botón. Si la imagen no tiene origen (source), la columna de la imagen desaparece por completo.

Esto evita que aparezcan elementos HTML vacíos en la página — sin diseños rotos, sin divs vacíos ocupando espacio, sin problemas de accesibilidad debido a etiquetas de título vacías.

Mejores Prácticas para Imágenes en HubSpot

Al usar el tipo de campo de imagen de HubSpot, muestra siempre los valores del campo directamente — src, alt, width y height. El campo de imagen de HubSpot ya está optimizado para SEO. Proporciona atributos de imagen responsivos y un manejo adecuado del texto alternativo (alt) de forma nativa.

No sobrescribas estos valores con atributos codificados de forma rígida (hardcoded). Simplemente añade loading="lazy" para las imágenes que están por debajo de la línea de pliegue (below-the-fold) para mejorar el rendimiento de la página.

Uso de Macros para Configuraciones de Sección Reutilizables

Si cada módulo necesita configuraciones de sección (ID, clase, visibilidad), no deberías copiar ese código en cada module.html. En su lugar, crea un archivo de macro una vez e impórtalo en todas partes.

Crea macros/section-macros.html en tu tema

{% module_block module "widget_f30f77d2-f420-436a-a37a-a1b595aa00f0" %}{% module_attribute "child_css" is_json="true" %}null{% end_module_attribute %}{% module_attribute "code_block" is_json="true" %}"{%- macro section_attrs(settings) -%}
  {%- if settings.section_id %} id=\"{{ settings.section_id }}\"{% endif -%}
  {%- if settings.section_class %} class=\"{{ settings.section_class }}\"{% endif -%}
{%- endmacro -%}"{% end_module_attribute %}{% module_attribute "css" is_json="true" %}null{% end_module_attribute %}{% module_attribute "label" is_json="true" %}null{% end_module_attribute %}{% module_attribute "module_id" is_json="true" %}321957934793{% end_module_attribute %}{% module_attribute "schema_version" is_json="true" %}2{% end_module_attribute %}{% module_attribute "tag" is_json="true" %}"module"{% end_module_attribute %}{% end_module_block %}

Importa y usa en cualquier módulo

{% module_block module "widget_cc7f645b-5dae-40a9-9015-e0b56b33041e" %}{% module_attribute "child_css" is_json="true" %}null{% end_module_attribute %}{% module_attribute "code_block" is_json="true" %}"{%- import '../macros/section-macros.html' as section_macros -%}

{%- if module.section_settings.section_visible -%}
<section {{ section_macros.section_attrs(module.section_settings) }}>
  {# Contenido del módulo aquí #}
</section>
{%- endif -%}"{% end_module_attribute %}{% module_attribute "css" is_json="true" %}null{% end_module_attribute %}{% module_attribute "label" is_json="true" %}null{% end_module_attribute %}{% module_attribute "module_id" is_json="true" %}321957934793{% end_module_attribute %}{% module_attribute "schema_version" is_json="true" %}2{% end_module_attribute %}{% module_attribute "tag" is_json="true" %}"module"{% end_module_attribute %}{% end_module_block %}

Escríbelo una vez. Impórtalo en 50 módulos. Cuando necesites cambiar cómo funcionan los IDs de sección, actualiza un archivo y cada módulo recibirá la actualización. Así es como se construyen los temas profesionales de HubSpot.

Estrategia CSS — Deja de Escribir CSS en el Módulo

Esta podría ser la sección más importante de toda esta guía. La mayoría de los desarrolladores de HubSpot escriben CSS dentro del archivo module.css de cada módulo. Esto crea un desastre — estilos duplicados en todos los módulos, espaciado inconsistente, tamaños de archivo crecientes y páginas que cargan más lento con cada nuevo módulo.

Usa Clases de Utilidad de Tailwind CSS en su Lugar

Mira el module.html de arriba. No hay CSS personalizado. Cada estilo se aplica a través de clases de utilidad de Tailwind directamente en el HTML:

  • max-w-7xl mx-auto px-4 — contenedor con ancho máximo, centrado, relleno horizontal
  • grid grid-cols-1 lg:grid-cols-2 gap-12 — cuadrícula responsiva de dos columnas
  • text-4xl sm:text-5xl lg:text-6xl font-bold — tamaño de título responsivo
  • hover:bg-blue-700 transition-colors — efecto hover con transición suave

Beneficios de este enfoque:

  • Un archivo CSS para todo el tema — y no 50 archivos module.css separados
  • Minificado y purgado — solo se incluyen las clases que realmente usas
  • Sistema de diseño consistente — cada módulo usa la misma escala de espaciado, paleta de colores y puntos de interrupción (breakpoints)
  • Mejor contraste de colores — la paleta predeterminada de Tailwind está diseñada teniendo en cuenta los estándares de accesibilidad WCAG AA
  • Cualquier desarrollador puede leerlo — sin necesidad de buscar en las hojas de estilo para entender qué hace una clase

Cuando Realmente Necesitas module.css

El único momento para escribir en module.css es para cosas que Tailwind genuinamente no puede manejar — típicamente animaciones personalizadas o patrones complejos de pseudo-elementos:

{% module_block module "widget_f68094d8-24ea-4121-90dc-f9e530823ca9" %}{% module_attribute "child_css" is_json="true" %}null{% end_module_attribute %}{% module_attribute "code_block" is_json="true" %}"@keyframes fadeInUp {
  from { opacity: 0; transform: translateY(20px); }
  to { opacity: 1; transform: translateY(0); }
}

.hero-animate {
  animation: fadeInUp 0.6s ease-out;
}"{% end_module_attribute %}{% module_attribute "css" is_json="true" %}null{% end_module_attribute %}{% module_attribute "label" is_json="true" %}null{% end_module_attribute %}{% module_attribute "module_id" is_json="true" %}321957934793{% end_module_attribute %}{% module_attribute "schema_version" is_json="true" %}2{% end_module_attribute %}{% module_attribute "tag" is_json="true" %}"module"{% end_module_attribute %}{% end_module_block %}

Todo lo demás debe estar en el CSS global de tu tema o ser manejado por utilidades de Tailwind. Punto final.

JavaScript — Solo Cuando Sea Necesario

La misma regla se aplica a module.js. La mayoría de los módulos no necesitan JavaScript en absoluto.

Módulos que no necesitan JS: secciones hero, bloques de texto e imagen, cuadrículas de características, tarjetas de testimonios, tablas de precios, secciones de pie de página. Estos son puramente visuales — el HTML y el CSS se encargan de todo.

Módulos que necesitan JS: acordeones, carruseles/sliders, componentes de pestañas, ventanas emergentes modales (pop-ups), validación de formularios, animaciones activadas por desplazamiento (scroll).

Cuando escribas JavaScript, mantenlo 'vanilla' (puro). Nada de jQuery en 2026. HubSpot carga module.js solo una vez por tipo de módulo, incluso si el módulo aparece varias veces en una página — pero aún así añade peso a tu página. Sé intencional sobre cada script que incluyas.

Contraste de Colores y Accesibilidad

Google considera las señales de accesibilidad en las clasificaciones de búsqueda. Construir módulos accesibles no es solo una buena práctica — impacta directamente en tu SEO.

Si estás usando Tailwind CSS, ya tienes una ventaja. La paleta de colores predeterminada de Tailwind está diseñada con proporciones de contraste adecuadas. Apégate a estas combinaciones seguras:

  • text-slate-900 sobre bg-white — proporción de contraste 15.4:1 (pasa AAA)
  • text-white sobre bg-slate-900 — proporción de contraste 15.4:1 (pasa AAA)
  • text-slate-700 sobre bg-slate-50 — proporción de contraste 8.1:1 (pasa AAA)

Evita el texto claro sobre fondos claros. Una proporción de contraste inferior a 4.5:1 no pasa WCAG AA y puede perjudicar tus clasificaciones de búsqueda.

Lista de Verificación de Accesibilidad del Módulo

  • Toda imagen tiene un atributo alt significativo proveniente del campo de imagen de HubSpot
  • La jerarquía de títulos es lógica — H1 seguido de H2, nunca saltando directamente a H4
  • Los enlaces tienen texto descriptivo — "Lee la guía completa" y no "Haz clic aquí"
  • Los elementos interactivos usan etiquetas HTML adecuadas — <a> para enlaces, <button> para acciones
  • El color por sí solo nunca transmite significado — combina siempre el color con texto o iconos

Probando tus Módulos

Antes de publicar cualquier módulo, revisa esta lista de verificación:

  • Previsualiza el módulo en la vista previa del Administrador de Diseño de HubSpot
  • Prueba en una página real con contenido real — no solo texto de relleno (dummy text)
  • Verifica los cuatro puntos de interrupción (breakpoints): móvil (320px), tablet (768px), laptop (1024px), escritorio (1440px)
  • Verifica que solo exista un H1 en la página cuando este módulo esté presente
  • Prueba con campos vacíos — ¿el diseño sigue luciendo limpio?
  • Prueba con contenido muy largo — ¿el título se ajusta a la línea correctamente?
  • Ejecuta una auditoría de Lighthouse para obtener puntuaciones de Rendimiento, Accesibilidad y SEO
  • Verifica el código fuente de la página — ¿hay líneas en blanco innecesarias o elementos vacíos?

Errores Comunes que Perjudican tus Módulos

Escribir CSS en cada módulo. Esto crea estilos duplicados y cargas de página más pesadas. Usa clases de utilidad de Tailwind o escribe CSS global en la hoja de estilos de tu tema. El CSS del módulo debe ser tu último recurso.

Añadir JavaScript innecesariamente. Los módulos de contenido estático como las secciones hero, las cuadrículas de características y los testimonios no necesitan JavaScript. Cada script añade peso a la página.

Codificar de forma rígida (hardcoding) las etiquetas de título. Si escribes <h2> directamente en tu HTML, los editores de contenido no podrán controlar la jerarquía SEO. Siempre usa un campo de opción y renderiza la etiqueta dinámicamente.

Ignorar el espacio en blanco de HubL. Usar {% %} en lugar de {%- -%} llena tu HTML renderizado con líneas en blanco. La sintaxis de guiones elimina los espacios en blanco y produce una salida más limpia y ligera.

Listas de campos planas sin grupos. Veinte campos sin agrupar en el editor de páginas es una pesadilla para los editores de contenido. Agrupa los campos relacionados — Contenido, Imagen, Botón, Configuración de la Sección — para que la interfaz sea limpia y navegable.

Ignorar la configuración de la sección. Sin un campo de ID, los editores no pueden crear enlaces ancla. Sin un interruptor de visibilidad, no pueden ocultar una sección temporalmente. Sin un campo de clase personalizada, necesitan un desarrollador para cada ajuste de estilo. Añade estos tres campos a cada módulo como práctica estándar.

Sobrescribir el fragmento de imagen de HubSpot. El tipo de campo de imagen de HubSpot ya genera una salida optimizada para SEO con atributos adecuados de src, alt, width, height y responsividad. No reemplaces esto con código personalizado — simplemente muestra los valores del campo directamente y añade loading="lazy" cuando sea apropiado.

Usar CTAs para cada botón. Los CTAs de HubSpot tienen una sobrecarga de seguimiento. Para botones simples que enlazan a una página, usa una etiqueta ancla con un campo de texto y un campo de enlace en su lugar. Reserva los CTAs para botones que realmente necesitan seguimiento y análisis.

Conclusión

Construir módulos personalizados en HubSpot no se trata solo de hacer que las cosas se vean bien — se trata de construir componentes que sean rápidos, accesibles, compatibles con SEO y fáciles de usar para los editores de contenido.

Las técnicas cubiertas en esta guía — campos agrupados, etiquetas de título dinámicas, control de espacios en blanco de HubL, clases de utilidad de Tailwind, macros reutilizables y manejo adecuado de imágenes — son lo que separa el desarrollo profesional de HubSpot del trabajo de aficionados.

Comienza a aplicar estas prácticas a tu próximo módulo. Tus editores de contenido te lo agradecerán, tus páginas cargarán más rápido y tus puntuaciones de SEO mejorarán.


Construir módulos de HubSpot desde cero lleva tiempo. Rapid te permite subir cualquier captura de pantalla de interfaz (UI) y genera un módulo de HubSpot completo y listo para producción — con fields.json, enlaces HubL y la estructura correcta — en menos de 60 segundos. Pruébalo gratis →

Share this article

¿Sigues creando módulos manualmente?

Convierte capturas de pantalla en módulos de HubSpot listos para usar con IA.