CodeGym /Cursos /ChatGPT Apps /Patrones inline: tarjetas, listas, carruseles y CTA

Patrones inline: tarjetas, listas, carruseles y CTA

ChatGPT Apps
Nivel 8 , Lección 1
Disponible

1. Qué es el modo inline y por qué es el «predeterminado»

Las directrices oficiales de OpenAI subrayan: la visualización inline es el modo principal para las ChatGPT Apps. El widget inline se renderiza directamente en el feed del chat encima de la respuesta del modelo e incluye un pequeño bloque de UI (tarjeta, lista, carrusel) más un mensaje de seguimiento (follow‑up) del GPT debajo.

La idea es sencilla: en lugar de llevar al usuario a una interfaz grande separada, le damos un «destello» visual compacto justo en el contexto de la conversación: el modelo explica qué ha pasado y qué opciones hay a continuación, y el widget muestra cuidadosamente la estructura y las acciones disponibles.

Un widget inline:

  • es ligero en contenido y no requiere muchos pasos;
  • no arrastra al usuario a una navegación compleja con pestañas y barras de desplazamiento internas;
  • resuelve una o dos tareas pequeñas: mostrar una lista de opciones, permitir elegir, confirmar una acción, mostrar el estado.

El modo fullscreen (sobre él en la próxima lección) se necesita para asistentes grandes y contenido complejo. Ahora lo importante es otra cosa: por defecto, piensa en inline, y activa fullscreen conscientemente cuando inline ya no dé la talla.

Nuestra tarea en esta lección es aprender a manejar con seguridad los tres patrones inline principales y, sobre ellos, trabajar con CTA:

  • tarjetas
  • listas
  • carruseles

y además añadirles correctamente botones CTA (Call to Action).

2. Cuándo inline es mejor que fullscreen

Simplificando, el modo inline es un «ayudante rápido», y fullscreen es «una aplicación aparte dentro de ChatGPT».

Inline brilla especialmente cuando:

  • hay que mostrar varias alternativas y permitir elegir una o dos;
  • el resultado se puede expresar en una estructura compacta: tarjeta de regalo, resumen del pedido, mini‑tabla;
  • el usuario realiza una acción corta: «elegir», «mostrar más detalles», «cambiar el filtro»;
  • el diálogo sigue siendo lo principal: GPT explica, bromea, comenta, y el widget solo ofrece un formulario o visual cómodo.

En términos de GiftGenius, inline es:

  • mostrar 3–5 mejores regalos para la persona elegida;
  • dar un filtro rápido: «muestra solo regalos digitales»;
  • confirmar la elección: «aquí tienes el resumen del pedido, ¿todo bien?».

Fullscreen te servirá más adelante para un asistente de pago complejo de tres pasos. Ahora nos quedamos en la zona ligera: una llamada de herramienta → un widget inline.

Para mayor claridad — una tabla pequeña:

Patrón Cuándo encaja perfectamente Ejemplo en GiftGenius
Tarjeta 1–3 entidades con parámetros clave y CTA 3 regalos destacados
Lista 5–10 elementos de texto; la legibilidad es clave lista de ideas sin imágenes
Carrusel 3–8 variantes similares con visual; se requiere desplazamiento lista larga de regalos

Entendido esto, bajemos a la concreción: cómo se materializan estos patrones en el UI y en el código. Seguiremos el mismo formato para cada patrón: primero — qué es desde el punto de vista de UX, luego — un componente sencillo de React para GiftGenius y, por último, cómo se integra todo en el widget inline.

3. Tarjetas: el ladrillo principal del UI inline

Qué es una tarjeta en el contexto del Apps SDK

Según las directrices de OpenAI, una tarjeta inline es un widget ligero, para un único usuario, que muestra una pequeña cantidad de datos estructurados y 1–2 acciones en la parte inferior. Puede tener un título, una imagen, un par de líneas de metadatos y un botón CTA principal (más uno secundario opcional).

En GiftGenius, cada tarjeta es un regalo. En ella es cómodo incluir:

  • título del regalo;
  • precio;
  • para quién encaja (por ejemplo, «compañero», «amigo cercano»);
  • una breve explicación de por qué es una buena opción;
  • un botón «Elegir este regalo» o «Más detalles».

La tarjeta debe ser autosuficiente: con un vistazo, el usuario ya entiende qué entidad tiene delante y cuál es la acción principal.

Tipo de datos y un componente simple GiftCard

Primero definimos el tipo de datos para un regalo. Supongamos que ya tenemos un ToolOutput con un array de estos objetos; aquí solo nos interesa la parte de UI.

// Estructura general del regalo para el UI
export type GiftSuggestion = {
  id: string;
  title: string;
  priceLabel: string;         // por ejemplo "≈ 40 $"
  recipientLabel: string;     // "para un compañero"
  reason?: string;            // explicación del modelo
  imageUrl?: string;
};

Ahora hagamos un componente sencillo de React para la tarjeta:

type GiftCardProps = {
  gift: GiftSuggestion;
  onSelect: (gift: GiftSuggestion) => void;
};

export function GiftCard({ gift, onSelect }: GiftCardProps) {
  return (
    <div className="flex flex-col gap-2 rounded-lg border p-3">
      <div className="text-sm font-medium">{gift.title}</div>
      <div className="text-xs text-muted-foreground">
        Para: {gift.recipientLabel} · {gift.priceLabel}
      </div>
      {gift.reason && (
        <div className="text-xs text-muted-foreground">{gift.reason}</div>
      )}
      <button
        className="mt-2 self-start rounded bg-primary px-3 py-1 text-xs text-primary-foreground"
        onClick={() => onSelect(gift)}
      >
        Elegir este regalo
      </button>
    </div>
  );
}

Algunas consideraciones de inmediato:

  • no sobrecargamos la tarjeta con texto; como máximo 2–3 líneas de metadatos y una explicación corta;
  • una CTA principal — «Elegir este regalo»; no intentamos meter 5 variantes distintas aquí;
  • el componente se reutiliza fácilmente tanto en una lista inline como dentro de un carrusel.

Cómo encajan las tarjetas en el widget general

Supongamos que tenemos un array gifts obtenido tras llamar a la herramienta giftgenius.suggestGifts a través de nuestro MCP. El widget en modo inline puede simplemente renderizarlas en una cuadrícula de 1–3 columnas.

type GiftGridProps = {
  gifts: GiftSuggestion[];
  onSelect: (gift: GiftSuggestion) => void;
};

export function GiftGrid({ gifts, onSelect }: GiftGridProps) {
  return (
    <div className="grid gap-3 sm:grid-cols-2">
      {gifts.map((gift) => (
        <GiftCard key={gift.id} gift={gift} onSelect={onSelect} />
      ))}
    </div>
  );
}

Aquí:

  • usamos una cuadrícula de 1–2 columnas para no convertir el widget en un «muro de ladrillos»;
  • podemos limitar fácilmente el número de tarjetas, por ejemplo, mostrando solo las primeras 3–6.

El propio manejador onSelect puede llamar a la herramienta de checkout o simplemente guardar la elección en el estado del widget y dejar que el modelo continúe el diálogo. Un ejemplo sencillo de integración con una herramienta:

async function handleSelect(gift: GiftSuggestion) {
  await window.openai.actions.call("giftgenius.startCheckout", {
    giftId: gift.id,
  });
}

Aquí window.openai.actions.call es el puente para llamar a una herramienta MCP registrada directamente desde el widget.

Normalmente, tras esa llamada el modelo mostrará o bien un estado o abrirá el siguiente widget (por ejemplo, el resumen del pedido). Lo principal — no intentar hacer todo el checkout con la lógica dentro de la tarjeta; la tarjeta debe lanzar el siguiente paso claro.

4. Listas: cuando lo visual no es lo principal

Si la tarjeta es un pequeño «póster», la lista es un listado de texto limpio. La documentación y las recomendaciones de UX muestran que las listas son adecuadas cuando importa más el contenido del texto que un acento visual llamativo.

Una lista encaja cuando:

  • hay que mostrar 5–10 alternativas, pero no requieren imagen;
  • el usuario quiere simplemente «echar un vistazo» a los títulos y descripciones breves;
  • las acciones son iguales para todos los elementos y el UI no debe distraer.

Ejemplos en GiftGenius:

  • lista de ideas «rápidas» de regalos sin detalles;
  • lista de categorías favoritas: «para compañeros», «para padres», «para niños»;
  • lista de selecciones guardadas («Regalos para el departamento de RR. HH.», «Detalles navideños hasta 20 $»).

Un componente de lista sencillo

Hagamos una lista compacta con un único botón CTA «Más detalles» a la derecha.

type GiftListProps = {
  gifts: GiftSuggestion[];
  onSelect: (gift: GiftSuggestion) => void;
};

export function GiftList({ gifts, onSelect }: GiftListProps) {
  return (
    <ul className="flex flex-col gap-2">
      {gifts.map((gift) => (
        <li
          key={gift.id}
          className="flex items-center justify-between rounded-md border px-3 py-2 text-sm"
        >
          <span className="truncate">{gift.title}</span>
          <button
            className="text-xs text-primary"
            onClick={() => onSelect(gift)}
          >
            Más detalles
          </button>
        </li>
      ))}
    </ul>
  );
}

Aquí hacemos:

  • damos a la cabecera truncate para que los títulos largos no rompan el diseño;
  • usamos de nuevo una única CTA por elemento;
  • dejamos toda la información «rica» (descripción, imagen, reseñas) para el siguiente paso — por ejemplo, al hacer clic, abrir una tarjeta aparte o una visualización en fullscreen.

La lista se combina especialmente bien con las propuestas de follow‑up del GPT. El widget muestra la lista de «candidatos», y debajo el GPT escribe algo como:

«Puedo acotar a regalos de hasta 30 $ o mostrar solo los digitales. ¿Qué elegimos?» y ofrece dos o tres botones de follow‑up.

En una sección aparte veremos exactamente cómo es mejor combinar los widgets inline y los mensajes de follow‑up en distintos escenarios.

5. Carruseles: cuando hay muchas opciones, pero todas se parecen

Un carrusel es un conjunto de tarjetas colocadas horizontalmente y que se pasan con un gesto o con botones de navegación. Las directrices recomiendan usar carruseles cuando muestras una lista pequeña de elementos similares (normalmente 3–8), cada uno con una imagen, un título y algunos metadatos.

La idea principal: el usuario puede escanear rápidamente el conjunto de opciones sin quedarse dormido ante una lista vertical infinita.

En GiftGenius, un carrusel viene bien si:

  • tenemos 10–15 regalos adecuados, pero el widget inline solo debe mostrar «los ocho calientes»;
  • cada regalo es agradable visualmente (imagen, presentación);
  • importa que el usuario pase las opciones sin bajar mucho en el chat.

Reglas de UX para carruseles

Basado en directrices e investigación:

  • el número de tarjetas en el carrusel — de 3 a 8; si hay más, mejor dar un comando aparte «Mostrar más»;
  • cada tarjeta:
    • debe tener una imagen u otro elemento visual;
    • no debe contener más de dos líneas de texto‑metadatos;
    • tiene una CTA clara, por ejemplo «Elegir» o «Más detalles»;
  • nada de navegaciones anidadas complejas (pestañas, subflujos) dentro de la tarjeta;
  • evitamos barras de desplazamiento internas (verticales): que la altura de la tarjeta se adapte hasta un límite razonable, pero sin su propia barra de desplazamiento.

Carrusel sencillo con el principio «una tarjeta cada vez»

Para no meterse con el scroll horizontal complejo, se puede implementar la variante más simple: mostrar una tarjeta cada vez y dar botones «anterior/siguiente».

import { useState } from "react";

type GiftCarouselProps = {
  gifts: GiftSuggestion[];
  onSelect: (gift: GiftSuggestion) => void;
};

export function GiftCarousel({ gifts, onSelect }: GiftCarouselProps) {
  const [index, setIndex] = useState(0);
  const gift = gifts[index];

  return (
    <div className="flex flex-col gap-2">
      <GiftCard gift={gift} onSelect={onSelect} />
      <div className="flex items-center justify-between text-xs">
        <button
          disabled={index === 0}
          onClick={() => setIndex((i) => i - 1)}
        >
          ← Anterior
        </button>
        <span>
          {index + 1} / {gifts.length}
        </span>
        <button
          disabled={index === gifts.length - 1}
          onClick={() => setIndex((i) => i + 1)}
        >
          Siguiente →
        </button>
      </div>
    </div>
  );
}

Esto ya transmite la sensación de «carrusel», y además:

  • el código sigue siendo compacto;
  • no hace falta pelear con el ancho del contenedor ni con scrolls horizontales dentro del widget;
  • es fácil limitar gifts a 8 elementos antes de pasarlos al componente.

Si quieres un carrusel más «real», puedes usar overflow-x-auto y un ancho fijo de las tarjetas, pero es de esos casos en los que es más sencillo tomar un componente ya hecho de una biblioteca de UI (shadcn/ui, soluciones compatibles con Radix, etc.) que inventar el tuyo desde cero.

6. Botones CTA: pocos, claros, al grano

Las CTA (Call to Action) son el corazón de cualquier patrón inline. Son los botones los que convierten tu widget de una imagen en una herramienta operativa.

Principios básicos

La documentación de OpenAI da recomendaciones bastante estrictas:

  • en una tarjeta — como máximo dos botones primarios (uno principal, el segundo — secundario);
  • en un carrusel — en lo posible, una CTA por elemento;
  • el texto de la CTA debe ser un verbo concreto: «Mostrar detalles», «Añadir a la lista», «Ir al pago», y no algo abstracto como «Ok» o «Acción».

Cuantos menos botones, más fácil para el modelo y para el usuario. No olvides que encima y debajo del widget hay parte de texto en la respuesta, además de propuestas de follow‑up del GPT.

Vinculación de las CTA a la lógica de la aplicación

En nuestro GiftGenius, la mayoría de las CTA o bien:

  • cambiarán filtros/criterios de selección (nueva tool‑call giftgenius.refineSearch),
  • lanzarán el checkout (giftgenius.startCheckout),
  • abrirán un sitio externo (a través de openExternal, ya conocido por ti de lecciones anteriores).

Ejemplo de un manejador sencillo para la CTA «Cambiar filtros»:

async function handleRefineFilters(gift: GiftSuggestion) {
  await window.openai.actions.call("giftgenius.refineSearch", {
    baseGiftId: gift.id,
  });
}

Desde el punto de vista de UX es muy importante explicitar en las instrucciones del sistema cuándo y qué botones CTA debe proponer el modelo. Por ejemplo:

  • si el usuario pide «muestra más opciones», mejor mostrar un nuevo carrusel con el botón «Elegir»;
  • si ha llegado el momento de comprar, la CTA «Ir al pago» debe llevar a una tool‑call que inicie el checkout ACP (llegaremos a ello en el módulo sobre comercio y pagos).

Otra práctica útil — no duplicar las funciones de ChatGPT en las CTA. No hace falta poner un botón «Preguntar a ChatGPT», el usuario ya tiene un campo de entrada y voz. Las directrices recomiendan explícitamente evitar entradas «duplicadas» dentro de la tarjeta.

7. Inline + follow‑up: juego en pareja

Un widget inline nunca vive en el vacío. La estructura de la respuesta suele ser así:

  1. el modelo decide usar tu App y llamar a una herramienta;
  2. tu MCP devuelve los datos;
  3. ChatGPT renderiza el widget inline con esos datos;
  4. debajo, el modelo añade un pequeño texto de follow‑up y opciones listas para continuar.

Para GiftGenius, esto puede verse así:

  • widget inline: tres tarjetas de regalos con CTA «Elegir»;
  • texto debajo:
    «Aquí tienes tres ideas para un compañero: una lámpara de escritorio, un curso de hablar en público y una tarjeta regalo de una cafetería. Puedo: — mostrar solo opciones de hasta 30 $; — seleccionar algunas ideas más en un estilo similar; — ayudarte a ir directamente a la compra de una de ellas.»

El modelo, en el follow‑up, puede referirse a las CTA de tu widget («pulsa “Elegir” debajo de la opción que te guste») o proponer comandos de texto que lleven de nuevo a una tool‑call y a un nuevo render del UI inline.

Es importante recordar: el widget no tiene por qué saber hacerlo todo. A veces es mejor dejar parte del escenario al diálogo de texto y usar el widget como «bloque visual» dentro de la conversación.

8. Cómo se integra esto en el flujo general de GiftGenius

Para que quede más claro, resumamos todo en un diagrama de secuencia sencillo:

sequenceDiagram
  participant U as Usuario
  participant C as ChatGPT
  participant A as GiftGenius Widget
  participant B as MCP/Backend

  U->>C: "Elige 3 regalos de hasta 50 $ para un compañero"
  C->>B: call_tool(giftgenius.suggestGifts)
  B-->>C: 3 mejores opciones
  C->>A: render del widget inline (tarjetas/carrusel)
  A-->>U: tarjetas con CTA "Elegir"
  U->>A: clic en la CTA
  A->>B: call_tool(giftgenius.startCheckout)
  B-->>A: estado / enlace al pago
  A-->>U: resumen de la selección / estado
  C-->>U: follow-up: "Puedo encontrar más ideas o ayudar con la tarjeta de felicitación"

Desde el punto de vista arquitectónico:

  • el MCP sigue siendo el «cerebro» (selección, lógica de negocio, ACP),
  • el widget es la «cara» (tarjetas/listas/carruseles),
  • ChatGPT es el «conductor del diálogo», que explica qué ha pasado y propone los siguientes pasos.

Para que este flujo sea cómodo:

  • no sobrecargues el widget con acciones;
  • mantén los datos en las tarjetas compactos;
  • piensa qué opciones de follow‑up serán útiles tras cada visualización inline.

9. Un poco sobre el lado visual de los patrones inline

Hablaremos en detalle del diseño visual en una de las próximas lecciones del módulo, pero conviene mencionar ya algunos puntos críticos para los patrones inline.

Primero, asegúrate de que tus tarjetas y listas no parezcan un sitio ajeno dentro de ChatGPT. Los colores y los espacios deben ser cuidadosos, sin gradientes chillones ni fuentes Comic Sans. El widget inline es parte del UI general de ChatGPT, no un banner de 2007.

Segundo, evita las barras de desplazamiento internas. Si tu tarjeta es tan larga que aparece su propia barra de desplazamiento dentro, algo ha ido mal: o intentas meter demasiado contenido, o el patrón elegido es incorrecto (quizá necesitas fullscreen).

En tercer lugar, controla la densidad:

  • entre tarjetas debe haber un espacio visible;
  • la CTA debe ser fácilmente pulsable (padding adecuado);
  • el texto debe ser legible incluso en móvil, sin tipografías microscópicas.

Puede sonar a «manías de diseño», pero la práctica demuestra: si el widget inline parece «nativo», el modelo lo usa más a gusto y los usuarios se confunden menos.

10. Práctica: cómo desarrollar GiftGenius a partir de la lección

Si quieres afianzar el material, aquí tienes una checklist práctica sencilla:

Primero toma el resultado actual de la herramienta giftgenius.suggestGifts (array de regalos) y:

  1. Implementa tres variantes distintas de UI en un solo componente:
    • GiftGrid con tarjetas;
    • GiftList con lista de texto;
    • GiftCarousel con navegación «anterior/siguiente».
  2. Añádeles una o dos CTA:
    • para tarjetas — «Elegir»;
    • para la lista — «Más detalles»;
    • para el carrusel — también «Elegir», más un botón aparte debajo del widget «Mostrar más opciones».
  3. Según el estado (por ejemplo, cuántos regalos en total devolvió la herramienta) elige qué patrón usar:
    • si hay pocas opciones (≤ 3) — cuadrícula de tarjetas;
    • si hay muchas ideas de texto — lista;
    • si hay muchos regalos visuales — carrusel.

Así no solo practicarás UI, sino que empezarás a pensar en la elección dinámica del patrón según el contexto, lo que encantará tanto a los usuarios como a los revisores en la Store.

En general, los patrones inline son una capa de UI rápida y ligera que vive directamente en el feed del chat y no intenta sustituir a una aplicación aparte. Tarjetas, listas y carruseles cubren el 80% de los casos típicos: mostrar opciones, permitir elegir y continuar el diálogo con cuidado.

En la próxima lección de este módulo veremos qué hacer cuando inline ya «no da para más»: analizaremos asistentes fullscreen, el modo PiP y escenarios en los que tu App realmente necesita una pantalla grande aparte dentro de ChatGPT.

11. Errores típicos al trabajar con patrones inline

Error n.º 1: convertir el widget inline en un minisitio.
A veces los desarrolladores intentan meter en una sola tarjeta pestañas, acordeones, formularios, una tabla y un montón de elementos más. El resultado es un UI pesado que rompe el ritmo del chat y se vuelve incómodo en móvil. Las directrices dicen claramente: nada de navegaciones profundas ni vistas complejas dentro de tarjetas inline; los escenarios complejos se llevan a fullscreen.

Error n.º 2: demasiados botones CTA.
«Hagamos en la tarjeta “Más detalles”, “Comprar”, “Favoritos”, “Compartir”, “Reportar” y “Generar felicitación”». Al final el usuario se pierde, el modelo también, y la probabilidad de que pulsen el botón correcto cae. Recuerda la regla: una CTA principal y, como máximo, una secundaria. El resto de escenarios es mejor llevarlos al mensaje de follow‑up del GPT o a pasos posteriores.

Error n.º 3: mezclar lista, tarjetas y carrusel en una misma respuesta sin motivo.
Si el mismo contenido se muestra a veces como lista, a veces como tarjetas y a veces como carrusel «solo porque podemos», el usuario pierde la sensación de consistencia. Mejor elige un patrón para un tipo concreto de resultado (por ejemplo, ideas sin imágenes — lista; regalos con imágenes — carrusel) y síguelo.

Error n.º 4: hacer tarjetas sobrecargadas de texto.
Una tarjeta con tres párrafos de descripción, tres precios y dos bloques de «por qué es genial» se convierte en un muro de texto. El usuario deja de «escanearla» y simplemente pasa de largo. Intenta dejar en la tarjeta solo lo más importante: título, un parámetro clave, un motivo corto y la CTA. Todo lo demás se puede explicar en la respuesta de texto del GPT al lado.

Error n.º 5: confiar solo en el UI e ignorar el diálogo de follow‑up.
A veces se ve el enfoque de «todo lo hacemos con botones; el usuario no necesita hablar». Eso contradice la idea de ChatGPT. El widget inline debe complementar el diálogo, no sustituirlo. No olvides pensar qué opciones de follow‑up puede proponer el modelo debajo del widget: cambiar filtros, pedir más opciones, pasar al siguiente paso.

Error n.º 6: ignorar las limitaciones en el número de elementos.
Un carrusel de 25 tarjetas o una lista de 50 elementos dentro de un solo widget inline es el camino seguro a que el usuario haga scroll sin mirar. La documentación recomienda 3–8 elementos en carruseles y 5–10 posiciones en listas. Si hay más datos, es útil añadir una CTA como «Mostrar más» o «Mostrar todo en texto».

Error n.º 7: usar inline donde ya hace falta fullscreen.
A veces tienta «hacerlo todo inline», incluso cuando ya hay 4 pasos, formularios con decenas de campos y tablas grandes. O bien acabas creando un monstruo, o empiezas a inventar scrolls anidados y pseudopasos dentro de las tarjetas. En cuanto notes que los pasos y los campos se multiplican — es la señal para pensar en pasar a un asistente en fullscreen, y dejar inline para previsualizaciones rápidas y resúmenes de acciones.

Comentarios
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION