Ir al contenido principal

Sección 05

05 · Sections & Theme Blocks

Los LEGO del storefront, con un giro importante en 2025.

En una frase

Una section es la unidad de composición de una página — un componente con schema de configuración que el comerciante puede gestionar desde el Theme Editor. Los blocks son las subunidades dentro de una section. Desde 2025, los theme blocks reemplazan a los section blocks como modelo preferido: los blocks dejan de ser locales a una section y pasan a ser reutilizables en todo el theme.

Qué es y por qué existe

Una section es un archivo Liquid en sections/ con un bloque {% schema %} al final. El schema define el nombre de la section, sus settings (configuración de la instancia), y sus blocks (subunidades de contenido). Cuando un comerciante agrega una section a una página desde el Theme Editor, Shopify usa el schema para construir la interfaz de configuración — ningún código adicional necesario.

Hasta 2025, los blocks que podían existir dentro de una section eran siempre section blocks: definidos en el schema de esa section específica, locales a ella, sin posibilidad de reutilización. Si tenías un block “Historia de origen” que querías usar en la página de producto y también en una landing page, tenías que definirlo dos veces, en el schema de cada section.

En 2025, Shopify introdujo los theme blocks: blocks definidos en archivos independientes en el directorio /blocks/, reutilizables en cualquier section del theme. Un theme block es un componente de presentación — tiene su propio Liquid, su propio schema, sus propios settings — que cualquier section puede aceptar. El Horizon theme de 2025 está construido completamente sobre este modelo.

Esta distinción no es menor. Cambia la arquitectura de composición de un theme completo.

El modelo de section blocks (pre-2025)

Un section con blocks locales tiene este patrón. Este es un ejemplo ilustrativo — el archivo correspondiente en Brew Atlas sería ~/proyectos/shopify/brew-atlas/brew-atlas-theme/sections/features.liquid:

{%- comment -%} sections/features.liquid {%- endcomment -%}
<div class="features">
  {%- for block in section.blocks -%}
    {%- case block.type -%}
      {%- when 'feature_item' -%}
        <div class="feature" {{ block.shopify_attributes }}>
          <h3>{{ block.settings.title }}</h3>
          <p>{{ block.settings.description }}</p>
        </div>
    {%- endcase -%}
  {%- endfor -%}
</div>
 
{% schema %}
{
  "name": "Features",
  "blocks": [
    {
      "type": "feature_item",
      "name": "Feature",
      "settings": [
        { "type": "text", "id": "title", "label": "Título" },
        { "type": "textarea", "id": "description", "label": "Descripción" }
      ]
    }
  ],
  "presets": [
    {
      "name": "Features",
      "blocks": [
        { "type": "feature_item" },
        { "type": "feature_item" }
      ]
    }
  ]
}
{% endschema %}

Este modelo funciona, pero el block feature_item solo existe dentro de esta section. Si otra section necesita un componente similar, hay que redefinirlo. El mantenimiento de blocks duplicados en múltiples sections es el problema que los theme blocks resuelven.

El modelo de theme blocks (2025+)

Con theme blocks, la definición del block vive en /blocks/origin-story.liquid. Este es un ejemplo simplificado — el archivo completo con todos los settings de Brew Atlas está en la sección “Sintaxis y anatomía completa” más abajo.

Crea ~/proyectos/shopify/brew-atlas/brew-atlas-theme/blocks/origin-story.liquid:

{%- comment -%} theme/blocks/origin-story.liquid {%- endcomment -%}
<div class="origin-story" {{ block.shopify_attributes }}>
  <h3>{{ block.settings.heading }}</h3>
  <div class="origin-story__body">{{ block.settings.body }}</div>
</div>
 
{% schema %}
{
  "name": "Origin story",
  "settings": [
    {
      "type": "text",
      "id": "heading",
      "label": "Heading",
      "default": "From the farm"
    },
    {
      "type": "richtext",
      "id": "body",
      "label": "Story body"
    }
  ],
  "presets": [
    {
      "name": "Origin story",
      "category": "Editorial",
      "settings": {
        "heading": "From the farm"
      }
    }
  ]
}
{% endschema %}

Observa que el schema de un theme block no tiene "blocks" ni "presets" de sección — solo "settings" y sus propios "presets" (que son sugerencias de configuración inicial para el block picker). El archivo vive en /blocks/, no en /sections/.

Una section que acepta theme blocks

La section que consume theme blocks declara en su schema que acepta "@theme" como tipo de block. Este es un ejemplo simplificado — la versión completa de Brew Atlas está en la sección “Sintaxis y anatomía completa” más abajo.

Edita ~/proyectos/shopify/brew-atlas/brew-atlas-theme/sections/pdp.liquid (créalo si no existe aún):

{%- comment -%} theme/sections/pdp.liquid {%- endcomment -%}
<section class="pdp container">
  <div class="pdp__media">
    {%- if product.featured_image -%}
      <img
        src="{{ product.featured_image | image_url: width: 800 }}"
        width="800"
        height="800"
        alt="{{ product.featured_image.alt | escape }}"
        loading="eager"
      />
    {%- endif -%}
  </div>
 
  <div class="pdp__info">
    {%- if section.settings.show_vendor and product.vendor != blank -%}
      <p class="pdp__vendor">{{ product.vendor }}</p>
    {%- endif -%}
 
    <h1 class="pdp__title">{{ product.title }}</h1>
 
    <div class="pdp__price">
      {{ product.price | money }}
      {%- if product.compare_at_price > product.price -%}
        <span class="pdp__price--compare">{{ product.compare_at_price | money }}</span>
      {%- endif -%}
    </div>
 
    {% content_for 'blocks' %}
  </div>
</section>
 
{% schema %}
{
  "name": "PDP",
  "settings": [
    {
      "type": "checkbox",
      "id": "show_vendor",
      "label": "Mostrar proveedor",
      "default": true
    }
  ],
  "blocks": [{ "type": "@theme" }],
  "presets": [{ "name": "PDP default" }]
}
{% endschema %}

El tag {% content_for 'blocks' %} es el punto de inserción donde Shopify renderiza los theme blocks que el comerciante agregó a esta sección desde el Theme Editor. El schema declara "blocks": [{ "type": "@theme" }] — eso le dice a Shopify que esta section acepta cualquier theme block disponible en el theme, no solo tipos predefinidos.

Ahora el block “Origin story” puede aparecer en la sección PDP de la página de producto, y también en cualquier otra section del theme que acepte @theme. El comerciante puede agregar ese bloque a una landing page de café, a la página “Sobre nosotros”, o a cualquier otro lugar — desde el Theme Editor, sin código.

La regla que no tiene excepciones

Theme blocks y section blocks no pueden coexistir en el mismo schema de section. Si en el schema de una section declaras [{ "type": "@theme" }], no puedes también tener blocks locales definidos en ese mismo schema. Si lo intentas, la section no renderiza y el error en el Theme Editor no es siempre obvio.

La elección es: o la section usa el modelo de section blocks (defines todos los tipos localmente), o usa el modelo de theme blocks (acepta @theme, los tipos viven en /blocks/). No hay término medio.

Para themes nuevos en 2026: usa theme blocks. Para themes existentes en OS 2.0 que ya tienen section blocks: puedes migrar section por section — no tienes que migrar todo de una vez.

Static blocks

Una feature de 2025 que vale documentar: los static blocks. Son blocks con posición fija que el comerciante no puede mover — solo configurar. Útiles para elementos obligatorios de una section donde el orden importa (por ejemplo, el título de un producto siempre aparece antes del precio, pero el comerciante puede cambiar el texto del título).

Un static block se declara en el schema con "type": "@app" (para blocks de apps) o con una referencia directa al tipo, y se renderiza con {% content_for 'blocks', id: 'block-id', type: 'block-type' %} en lugar del content_for 'blocks' genérico.

Los static blocks son un refinamiento del modelo — la mayoría de los themes no los necesitan en sus primeras versiones.

Block picker y categorías

Cuando el comerciante hace clic en “Agregar bloque” en el Theme Editor, ve un picker de blocks disponibles. Las categorías que aparecen en ese picker vienen del campo "category" en los presets del schema de cada theme block.

Categorías comunes (las que reconoce Shopify y muestra agrupadas):

  • "Editorial" — texto, citas, historias
  • "Media" — imágenes, videos
  • "Commerce" — precio, selector de variantes, botón de compra
  • "Social" — compartir, redes sociales
  • "Trust" — reviews, garantías, sellos

Si no declaras "category", el block aparece en la categoría general sin agrupar. Para themes públicos o temas con muchos blocks, las categorías mejoran significativamente la experiencia del comerciante.

Puentes mentales

Desde Angular/React

Section blocks son como subcomponentes privados definidos dentro de un componente padre — tightly coupled. Si el componente FeaturesList define internamente un FeatureItem, ese FeatureItem no existe fuera de FeaturesList.

Theme blocks son como una librería de componentes compartida: OriginStory existe como componente independiente en la librería, y cualquier “sección” del theme puede importarlo y usarlo. En React sería la diferencia entre definir un componente dentro de otro archivo de componente vs exportarlo desde un shared component library.

En 2025, Shopify estandarizó en el modelo de librería compartida. Si construyes un theme nuevo, sigues ese modelo.

Estado 2026

Los theme blocks son la feature más importante del ecosistema de themes del último año. El estado actual:

  • Horizon (el reference theme oficial de Shopify, 2025) está construido íntegramente sobre theme blocks. Es la implementación de referencia.
  • Los section blocks siguen siendo válidos y soportados. No van a ser deprecados en el corto plazo — hay demasiados themes publicados que los usan. Pero para código nuevo, theme blocks son el camino.
  • El block picker en el Theme Editor se actualizó en 2025 para mostrar previews de blocks (thumbnails) cuando el schema del block los define. Para el App Store, los blocks con previews tienen mejor conversión.
  • Los static blocks llegaron en 2025 como refinamiento — úsalos cuando la posición de un elemento en la section es semántica y no debería ser modificable por el comerciante.

Sintaxis y anatomía completa

Para que quede claro el flujo completo, aquí están los dos archivos del paso de Brew Atlas con comentarios de propósito.

Crea ~/proyectos/shopify/brew-atlas/brew-atlas-theme/blocks/origin-story.liquid:

{%- comment -%} theme/blocks/origin-story.liquid {%- endcomment -%}
{%- comment -%}
  Theme block: Origin Story
  Propósito: mostrar la historia de origen del productor de café.
  Reutilizable en: PDP, landing pages de origen, página "Sobre nosotros".
  Variables disponibles: block.settings.* (schema abajo)
{%- endcomment -%}
 
<div class="origin-story" {{ block.shopify_attributes }}>
  {%- if block.settings.heading != blank -%}
    <h3 class="origin-story__heading">{{ block.settings.heading }}</h3>
  {%- endif -%}
 
  {%- if block.settings.body != blank -%}
    <div class="origin-story__body rte">
      {{ block.settings.body }}
    </div>
  {%- endif -%}
 
  {%- if block.settings.country != blank -%}
    <p class="origin-story__country">
      <span class="label">Origen</span>
      <span>{{ block.settings.country }}</span>
    </p>
  {%- endif -%}
</div>
 
{% schema %}
{
  "name": "Origin story",
  "settings": [
    {
      "type": "text",
      "id": "heading",
      "label": "Heading",
      "default": "From the farm"
    },
    {
      "type": "richtext",
      "id": "body",
      "label": "Story body"
    },
    {
      "type": "text",
      "id": "country",
      "label": "Country of origin"
    }
  ],
  "presets": [
    {
      "name": "Origin story",
      "category": "Editorial",
      "settings": {
        "heading": "From the farm"
      }
    }
  ]
}
{% endschema %}

Edita (o crea) ~/proyectos/shopify/brew-atlas/brew-atlas-theme/sections/pdp.liquid:

{%- comment -%} theme/sections/pdp.liquid {%- endcomment -%}
{%- comment -%}
  Section: PDP (Product Detail Page)
  Acepta: theme blocks (tipo @theme) — incluyendo origin-story y cualquier
          otro block del directorio /blocks/ del theme.
  Settings: show_vendor (checkbox), show_sku (checkbox)
{%- endcomment -%}
 
<section class="pdp container" id="pdp-{{ section.id }}">
  <div class="pdp__layout">
 
    <div class="pdp__media">
      {%- if product.featured_image -%}
        <img
          src="{{ product.featured_image | image_url: width: 800 }}"
          srcset="{{ product.featured_image | image_url: width: 400 }} 400w,
                  {{ product.featured_image | image_url: width: 800 }} 800w"
          sizes="(min-width: 1024px) 50vw, 100vw"
          width="800"
          height="800"
          alt="{{ product.featured_image.alt | escape }}"
          loading="eager"
          fetchpriority="high"
        />
      {%- endif -%}
    </div>
 
    <div class="pdp__info">
      {%- if section.settings.show_vendor and product.vendor != blank -%}
        <p class="pdp__vendor">{{ product.vendor }}</p>
      {%- endif -%}
 
      <h1 class="pdp__title">{{ product.title }}</h1>
 
      <div class="pdp__price">
        {%- if product.compare_at_price > product.price -%}
          <span class="price price--sale">{{ product.price | money }}</span>
          <span class="price price--compare">{{ product.compare_at_price | money }}</span>
        {%- else -%}
          <span class="price">{{ product.price | money }}</span>
        {%- endif -%}
      </div>
 
      {%- if section.settings.show_sku and product.selected_variant.sku != blank -%}
        <p class="pdp__sku">SKU: {{ product.selected_variant.sku }}</p>
      {%- endif -%}
 
      {%- comment -%}
        Punto de inserción de theme blocks.
        El comerciante agrega/reordena blocks desde el Theme Editor aquí.
      {%- endcomment -%}
      {% content_for 'blocks' %}
 
      <div class="pdp__actions">
        {%- form 'product', product -%}
          <input type="hidden" name="id" value="{{ product.selected_variant.id }}">
          <button type="submit" class="btn btn--primary">Agregar al carrito</button>
        {%- endform -%}
      </div>
    </div>
  </div>
</section>
 
{% schema %}
{
  "name": "PDP",
  "settings": [
    {
      "type": "checkbox",
      "id": "show_vendor",
      "label": "Mostrar proveedor",
      "default": true
    },
    {
      "type": "checkbox",
      "id": "show_sku",
      "label": "Mostrar SKU",
      "default": false
    }
  ],
  "blocks": [{ "type": "@theme" }],
  "presets": [{ "name": "PDP default" }]
}
{% endschema %}

Proyecto · Brew Atlas

Brew Atlas · Paso 5

Dos archivos nuevos que definen la arquitectura de composición del PDP de Brew Atlas:

Qué vas a crear/tocar en esta sección (archivos):

  • ~/proyectos/shopify/brew-atlas/brew-atlas-theme/blocks/origin-story.liquid — crea este archivo (theme block reutilizable). El contenido completo está en la sección “Sintaxis y anatomía completa” arriba.
  • ~/proyectos/shopify/brew-atlas/brew-atlas-theme/sections/pdp.liquid — crea (o edita si ya existe) este archivo. El contenido completo está en la misma sección arriba.

Las copias de referencia del tutorial viven en brew-atlas/theme/blocks/origin-story.liquid y brew-atlas/theme/sections/pdp.liquid (en este repo).

Comandos a ejecutar (orden): ninguno en esta sección — solo creación de archivos Liquid. El push al store lo haces en la sección 04 o cuando termines de crear todos los archivos del theme.

La decisión de usar theme blocks desde el inicio no es por completitud académica: es porque Brew Atlas tiene múltiples superficies (theme + headless con Hydrogen) y los theme blocks nos dan una separación de responsabilidades clara — el bloque origin-story define su propia lógica de presentación, y cualquier superficie que lo necesite puede usarlo sin reescribir nada.

Errores comunes

Cuidado

Mezclar theme blocks y section blocks en el mismo schema de section hace que la section no renderice. El schema no da un error explícito inmediatamente — la section simplemente no aparece en la página o no muestra ningún block. Si agregas "blocks": [{ "type": "@theme" }] al schema, elimina cualquier definición inline de blocks locales en ese mismo schema.

Cuidado

{{ block.shopify_attributes }} es obligatorio en el elemento raíz de un theme block si quieres que funcionen el drag-and-drop en el Theme Editor y las herramientas de inspección de Shopify. Sin ese atributo, el block se renderiza pero el Editor no puede identificarlo ni permitir su reordenamiento. Es un attributo HTML que Shopify inyecta con el ID del block.

Nota

Los theme blocks no reemplazan a los snippets. Un snippet es un partial de Liquid para reutilización de código — no tiene schema, no aparece en el Theme Editor, no puede ser configurado por el comerciante. Un theme block es una unidad de contenido configurable por el comerciante. Úsalos para cosas diferentes: snippets para lógica de presentación compartida, theme blocks para contenido editable.

Tip

Para depurar qué blocks tiene una section en el Theme Editor, agrega temporalmente {{ section.blocks | json }} en tu template Liquid. Verás exactamente qué blocks existen, qué tipos tienen, y qué settings tienen configurados. Elimínalo antes de hacer push a producción.

Checklist senior

Checklist senior

  • Puedes explicar la diferencia entre section blocks y theme blocks con un ejemplo concreto de cuándo usar cada uno
  • Sabes que theme blocks y section blocks no pueden coexistir en el mismo schema y entiendes por qué
  • Puedes escribir un theme block completo con schema, settings y presets
  • Sabes dónde va {% content_for 'blocks' %} y por qué es necesario
  • Entiendes que {{ block.shopify_attributes }} es obligatorio para que funcione el drag-and-drop en el Theme Editor
  • Puedes configurar una section para que acepte cualquier theme block del theme con "blocks": [{ "type": "@theme" }]
  • Sabes que Horizon (el reference theme de 2025) usa exclusivamente theme blocks y puedes citar eso como referencia de estilo
  • Puedes distinguir entre un snippet (reutilización de código, sin schema) y un theme block (unidad editable por el comerciante, con schema)

Quiz

Quiz · ¿Lo tenés claro?

5 preguntas · respondé para marcar la sección como completada.

  1. 1. ¿Cuál es la diferencia fundamental entre section blocks y theme blocks?

  2. 2. En el schema de una section quieres aceptar cualquier theme block. ¿Cuál es la sintaxis correcta?

  3. 3. Si pones section blocks y theme blocks en el mismo schema de section, ¿qué pasa?

  4. 4. ¿Qué hace `{% content_for 'blocks' %}` dentro del Liquid de una section?

  5. 5. Un block de theme no arranca/no se puede arrastrar en el Theme Editor. ¿Qué es lo más probable?

Siguiente

Con sections y theme blocks claros, la siguiente sección cubre metafields: el sistema de datos extendidos de Shopify. Los metafields son cómo agregas datos propios a las entidades de Shopify (productos, clientes, pedidos, colecciones) sin crear una base de datos externa. Para Brew Atlas, los metafields van a almacenar el origen, el tueste, las notas de cata y la frecuencia de suscripción de cada café — exactamente los datos que el block origin-story va a mostrar.