Ir al contenido principal

Sección 13

13 · Checkout UI Extensions

Personalización del checkout con targets y APIs.

En una frase

Las Checkout UI Extensions son componentes React — preact bajo el capó — que se renderizan en puntos específicos del checkout de Shopify. El checkout en sí mismo está bloqueado: no puedes modificar su HTML ni inyectar JavaScript arbitrario. Lo que sí puedes hacer es declarar una extensión que se monta en un “target” definido — antes de la dirección, después de los artículos del carrito, en la pantalla de confirmación — y que el comprador ve integrada como parte del flujo nativo.

Qué vas a ejecutar en esta sección

  1. shopify app generate extension --type checkout_ui --name gift-message — Scaffold de la extensión de checkout (una sola vez)
  2. shopify app dev — Preview local con el Checkout Editor del dev store
  3. shopify app deploy — Publicar la extensión al registro de Shopify

Buscá los bloques con badge ✓ Ejecutar en el contenido. Los bloques con 📖 Referencia son ilustrativos — no los ejecutes.

Qué es y por qué existe

El checkout de Shopify es intencionalmente hermético. Shopify no permite que código arbitrario de terceros se ejecute dentro del proceso de pago — las implicaciones de seguridad y de performance son demasiado severas. Lo que sí permite, desde el lanzamiento del modelo de Checkout Extensibility en 2023, es que las apps declaren extensiones que se montan en puntos de integración predefinidos y controlados.

Ese cambio liquidó checkout.liquid. La plantilla checkout.liquid era el mecanismo antiguo para personalizar el checkout en cuentas Shopify Plus — acceso completo al HTML del checkout, poder para inyectar scripts, cambiar el layout. Shopify la deprecó para cuentas Plus en 2024 y hoy está en modo legacy para todos los planes. El reemplazo es Checkout Extensibility: Checkout UI Extensions para la UI, Shopify Functions para la lógica.

La ventaja del modelo de extensiones sobre checkout.liquid no es solo para Shopify — también es para el desarrollador. Las extensiones son sandboxadas: no pueden romper el flujo de pago, no tienen acceso al DOM del checkout, y la API que exponen es curada y estable. Tus extensiones sobreviven a las actualizaciones del checkout de Shopify sin que tengas que mantenerlas activamente.

El runtime

La tecnología subyacente es preact, con una API de React compatible. Esto significa que escribes componentes como si fuera React, pero el runtime es preact — más ligero. Las extensiones se compilan a Wasm y se sirven desde la CDN de Shopify.

Los imports vienen del paquete @shopify/ui-extensions-react/checkout. NO importes de react directamente — el runtime no es React, y los imports de React pueden causar errores en tiempo de ejecución dentro del sandbox de la extensión.

Targets de extensión

Un “target” es el punto de montaje donde tu extensión aparece en el checkout. Hay dos categorías:

Block targets: el comerciante coloca tu extensión en el Checkout Editor, igual que coloca bloques en el Theme Editor. La extensión aparece donde el comerciante decide, dentro de la zona configurable. El target purchase.checkout.block.render es el más frecuente y el que usarás para features como “campo de mensaje de regalo”, “selector de fecha de entrega”, o “programa de puntos”.

Static targets: la extensión se monta automáticamente en una posición fija del checkout sin configuración del comerciante. Ejemplos:

  • purchase.checkout.delivery-address.render-before — antes del formulario de dirección
  • purchase.checkout.cart-line-item.render-after — después de cada artículo del carrito
  • purchase.checkout.contact.render-after — después del campo de email de contacto
  • purchase.checkout.reductions.render-before — antes de la sección de descuentos
  • purchase.checkout.payment-method-list.render-before — antes de los métodos de pago
  • purchase.thank-you.block.render — en la página de confirmación de pedido
  • purchase.order-status.block.render — en la página de estado del pedido

La lista completa de targets varía por versión de API — consulta la documentación oficial para ver todos los disponibles en 2026-04 y posteriores.

Estructura del proyecto

// brew-atlas-app/extensions/gift-message/ (directorio de la extensión)
extensions/
└── gift-message/
    ├── src/
    │   └── index.tsx
    ├── shopify.extension.toml
    └── package.json

A diferencia de las Functions (Rust), las Checkout UI Extensions tienen su propio package.json porque son proyectos de JavaScript/TypeScript. El CLI las instala como workspaces del monorepo del app.

El TOML de la extensión

# brew-atlas-app/extensions/gift-message/shopify.extension.toml
api_version = "2026-04"
 
[[extensions]]
name = "Gift message"
handle = "gift-message"
type = "ui_extension"
 
[[extensions.targeting]]
module = "./src/index.tsx"
target = "purchase.checkout.block.render"

Una sola extensión puede declarar múltiples [[extensions.targeting]] apuntando a distintos targets — por ejemplo, renderizar un campo en el checkout Y una confirmación en la thank-you page. Cada targeting tiene su propio módulo o puede apuntar al mismo.

La extensión: campo de mensaje de regalo

// brew-atlas-app/extensions/gift-message/src/index.tsx
import {
  reactExtension,
  BlockStack,
  Text,
  TextField,
  Divider,
  useApplyAttributeChange,
  useAttributeValues,
} from '@shopify/ui-extensions-react/checkout';
 
// El punto de entrada de la extensión declara el target y el componente raíz
export default reactExtension(
  'purchase.checkout.block.render',
  () => <GiftMessageExtension />,
);
 
function GiftMessageExtension() {
  // Leer el atributo actual del pedido (si el comprador ya escribió algo)
  const [giftMessage] = useAttributeValues(['gift_message']);
 
  // Callback para actualizar el atributo en el pedido
  const applyAttributeChange = useApplyAttributeChange();
 
  function handleChange(value: string) {
    applyAttributeChange({
      type: 'updateAttribute',
      key: 'gift_message',
      value: value,
    });
  }
 
  return (
    <BlockStack spacing="base">
      <Divider />
      <Text size="medium" emphasis="bold">
        Mensaje de regalo
      </Text>
      <Text size="small" appearance="subdued">
        Tu mensaje aparecerá en una tarjeta incluida en el envío.
      </Text>
      <TextField
        label="Mensaje (opcional)"
        value={giftMessage ?? ''}
        onChange={handleChange}
        multiLine={3}
        maxLength={200}
      />
    </BlockStack>
  );
}

El hook useAttributeValues lee los atributos de orden existentes. Los atributos de orden son pares clave-valor que viajan con el pedido — visibles para el comerciante en el Admin y accesibles desde el Admin API. useApplyAttributeChange los actualiza en tiempo real mientras el comprador escribe.

El package.json de la extensión

// brew-atlas-app/extensions/gift-message/package.json
{
  "name": "gift-message",
  "version": "0.0.1",
  "private": true,
  "scripts": {
    "build": "shopify app build --filter gift-message"
  },
  "devDependencies": {
    "@shopify/ui-extensions": "*",
    "@shopify/ui-extensions-react": "*",
    "typescript": "^5.0.0"
  }
}

Las versiones de @shopify/ui-extensions y @shopify/ui-extensions-react deben coincidir con la api_version declarada en el TOML. El CLI gestiona esto automáticamente cuando genera la extensión — si los actualizas manualmente, asegúrate de que la versión del paquete es compatible con la versión de API que declaraste.

APIs disponibles en el checkout

El paquete @shopify/ui-extensions-react/checkout expone docenas de hooks para leer el estado del checkout y proponer cambios:

Lectura de datos:

  • useCartLines() — las líneas del carrito con cantidades, variantes, y precios
  • useTotalAmount() — el total del pedido
  • useDeliveryGroups() — los grupos de entrega y métodos de envío seleccionados
  • useEmail() — el email de contacto del comprador
  • useLocalizationCountry() — el país de localización
  • useShippingAddress() — la dirección de envío (si el comprador la completó)
  • useAttributeValues(keys) — atributos de orden por clave

Cambios propuestos (requieren confirmación del runtime):

  • useApplyAttributeChange() — actualizar atributos del pedido
  • useApplyDiscountCodeChange() — añadir o quitar códigos de descuento
  • useApplyNoteChange() — actualizar la nota del pedido

Componentes UI disponibles: Los componentes exportados desde el paquete (BlockStack, InlineStack, Text, TextField, Button, Checkbox, Banner, Divider, Image, etc.) son los únicos elementos UI que puedes usar. No puedes usar HTML nativo (<div>, <p>) — estás dentro de un renderer controlado, no en el DOM del navegador directamente.

Restricciones del sandbox

Las Checkout UI Extensions corren en un sandbox con restricciones deliberadas:

Red: por defecto, las extensiones no pueden hacer requests HTTP. Para habilitar acceso a un servidor externo, debes declararlo en el TOML bajo [extensions.capabilities] con network_access = true. Las apps con network_access pasan por una revisión adicional para apps públicas.

DOM: no tienes acceso al DOM del checkout. Solo puedes usar los componentes del paquete de extensiones. No puedes ejecutar document.querySelector, window.location.href, ni ninguna API del browser directamente.

Datos: solo tienes acceso a los datos que los hooks exponen. No puedes leer campos del formulario de checkout que no estén en la API — como datos de tarjeta de crédito, por razones obvias.

Tiempo de ejecución: las extensiones tienen un timeout de ejecución. Las operaciones de larga duración (loops costosos, awaits largos) pueden causar que la extensión se descarte.

Activación por el comerciante

Después de hacer shopify app deploy, la extensión existe en el registro de Shopify pero no es visible en ningún checkout. El comerciante tiene que activarla desde el Checkout Editor en Admin → Configuración → Checkout → Personalizar.

Para block targets, el comerciante la añade desde el panel de bloques del editor — exactamente igual que añade un bloque de app en el Theme Editor. Para static targets, la activación puede ser automática o requerir habilitación explícita dependiendo del target.

Puentes mentales

Desde Chrome Extensions / Browser Extensions

Las Checkout UI Extensions son a Shopify lo que las extensiones de navegador son a Chrome: código sandboxado que extiende una experiencia de usuario bloqueada. No puedes modificar el HTML del checkout, igual que una extensión de Chrome no puede reemplazar el motor de renderizado del navegador. Lo que sí puedes hacer es añadir UI en puntos de integración predefinidos (targets / content scripts en URIs específicos) y leer un subconjunto controlado del estado de la página (hooks de checkout / APIs de Chrome Extension). La restricción no es arbitraria — es lo que hace posible que Shopify garantice que ninguna extensión puede romper el proceso de pago.

Estado 2026

Lo relevante al 2026:

  • checkout.liquid está muerto: para cuentas Plus fue deprecado en 2024. Para todos los planes, el modelo de Checkout Extensibility reemplaza cualquier personalización legacy. No hay razón para usar checkout.liquid en código nuevo.
  • API 2026-04 es la versión estable actual para extensiones de checkout. Las versiones 2025-07, 2025-10 siguen siendo válidas y soportadas. Si creas una extensión nueva, usa 2026-04.
  • Nuevos targets en 2026: los targets de “order status” (purchase.order-status.block.render) y “thank you” (purchase.thank-you.block.render) están GA. Úsalos para continuidad post-compra — confirmaciones, upsells, contenido de onboarding.
  • Performance: las extensiones se compilan a Wasm y se pre-renderizan. Shopify audita el bundle size de las extensiones en el proceso de revisión de apps públicas. Mantén las dependencias mínimas.

Proyecto · Brew Atlas

Brew Atlas · Paso 13

La extensión gift-message de Brew Atlas añade un campo de mensaje de regalo en el checkout. El comerciante la activa desde el Checkout Editor y la coloca donde prefiera dentro del flujo.

El mensaje queda guardado como atributo del pedido (gift_message) y es visible para el equipo de fulfillment en el detalle del pedido en el Admin. También es accesible desde el Admin GraphQL API en order.attributes.

Los archivos están en ~/proyectos/shopify/brew-atlas/brew-atlas-app/extensions/gift-message/. Para desplegar y previsualizar:

Desde ~/proyectos/shopify/brew-atlas/brew-atlas-app/:

Ejecutar Preview en el dev store

shopify app dev

Desde ~/proyectos/shopify/brew-atlas/brew-atlas-app/:

Ejecutar Deploy a produccion

shopify app deploy

Después del deploy, el comerciante activa la extensión desde Admin → Configuración → Checkout → Personalizar → Add block → Apps → Gift message.

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

  • ~/proyectos/shopify/brew-atlas/brew-atlas-app/extensions/gift-message/src/index.tsx — la extensión de checkout.
  • ~/proyectos/shopify/brew-atlas/brew-atlas-app/extensions/gift-message/shopify.extension.toml — target y metadatos.
  • ~/proyectos/shopify/brew-atlas/brew-atlas-app/extensions/gift-message/package.json — dependencias del paquete.

Comandos a ejecutar (orden):

  1. shopify app generate extension --type checkout_ui --name gift-message desde ~/proyectos/shopify/brew-atlas/brew-atlas-app/ (scaffold, una sola vez).
  2. shopify app dev desde ~/proyectos/shopify/brew-atlas/brew-atlas-app/ (preview local).
  3. shopify app deploy desde ~/proyectos/shopify/brew-atlas/brew-atlas-app/ (publicar).

Errores comunes

Cuidado

Los atributos de pedido que escribes con useApplyAttributeChange son visibles para el comerciante en el Admin y en cualquier integración que lea el pedido via Admin API. No los uses para datos que el comprador no debería saber que estás guardando, y no los uses para datos sensibles (ningún identificador personal más allá del flujo de pedido). Los atributos de pedido son parte del registro público del pedido, no un campo privado de la extensión.

Cuidado

No importes de react directamente en una Checkout UI Extension. El runtime es preact y el paquete @shopify/ui-extensions-react ya proporciona el wrapper correcto. Importar React directamente puede causar que el bundle crezca innecesariamente o que fallen las invocaciones del runtime en el sandbox de Shopify.

Nota

Las extensiones de checkout necesitan activación manual por el comerciante en el Checkout Editor después de cada deploy. El deploy sube la extensión al registro, pero no la activa automáticamente en los checkouts existentes. Para apps públicas, incluye instrucciones de activación en el proceso de onboarding de tu app — muchos comerciantes instalan la app y esperan que “funcione solo” sin entender que hay un paso de configuración en el Checkout Editor.

Tip

Para testear tu extensión sin desplegar, usa el comando de preview del CLI: shopify app dev levanta un servidor local y muestra un link de preview del checkout editor del dev store. Puedes hacer cambios en el código y ver el resultado en el editor en tiempo casi real. Los errores de compilación de TypeScript aparecen en la terminal — leer esos errores es más rápido que depurar en el browser.

Checklist senior

Checklist senior

  • Puedes explicar por qué checkout.liquid está deprecado y qué lo reemplaza en el modelo de Checkout Extensibility
  • Conoces la diferencia entre block targets y static targets: cuándo el comerciante configura la posición y cuándo es automática
  • Sabes los hooks principales: useCartLines, useApplyAttributeChange, useAttributeValues, y en qué casos usar cada uno
  • Entiendes por qué no puedes usar HTML nativo ni APIs del browser directamente en una extensión de checkout
  • Sabes que network_access = true en el TOML es necesario para que la extensión pueda hacer requests HTTP
  • Entiendes que los atributos de pedido son visibles para el comerciante y en el Admin API — no son datos privados
  • Sabes que después de shopify app deploy, el comerciante debe activar la extensión desde el Checkout Editor
  • Puedes usar el preview del CLI (shopify app dev) para iterar sobre la extensión sin desplegar

Quiz

Quiz · ¿Lo tenés claro?

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

  1. 1. ¿Por qué `checkout.liquid` está deprecado y qué lo reemplaza?

  2. 2. ¿Cuál es la diferencia entre un block target y un static target?

  3. 3. Quieres hacer fetch a tu backend desde una Checkout UI Extension. ¿Qué paso es obligatorio?

  4. 4. Tu extensión necesita guardar 'coffee_notes' en el pedido para que el merchant lo vea. ¿Cómo?

  5. 5. Desplegaste la extensión con `shopify app deploy`. El merchant no la ve en su checkout. ¿Qué falta?

Siguiente

Las Checkout UI Extensions cubren la experiencia del comprador durante el proceso de pago. El siguiente bloque completa la historia de extensiones de UI abordando el otro lado del customer journey: las Customer Account UI Extensions, que permiten a tu app añadir funcionalidad en el área de cuenta del cliente — subscripciones, historial personalizado, acciones post-compra.