Saltar a contenido

Breadcrumbs de categoría y de ficha: evaluación de propuestas

De dónde sacar la jerarquía de categorías para dibujar la ruta de navegación ("Inicio › Informática › Laptops › Notebooks › Producto") en la página de categoría y en la ficha del producto. Se evalúan cuatro fuentes posibles —Medusa, Payload, Algolia y el recorrido del visitante— y una combinación de Medusa con Payload.

Resumen

Se recomienda la propuesta A: el árbol de categorías de Medusa, con un único componente de breadcrumb para la categoría y la ficha, y el mismo dato en el JSON-LD. Es la única fuente que hoy está completa (135 categorías, hasta 3 niveles), que tiene una página a la que enlazar por cada nivel y que ya usa Google. No pide datos nuevos ni depende de otro equipo, y se estima en menos de un día.

Payload (propuesta B) es la opción correcta para lo que el negocio controla, que es el menú. Para el breadcrumb todavía no alcanza: tiene 17 categorías cargadas a mano, la sincronización con el catálogo está pendiente y no hay forma de saber a qué categoría de Payload pertenece un producto. Se propone como evolución (propuesta D), cuando la sincronización exista.

Situación actual

Dónde Qué hay Problema
Página de categoría (/py/categories/…) Breadcrumb visible "Inicio / Padre / Categoría", en modules/categories/templates/algolia-category-template.tsx El padre nunca aparece: getCategoryByHandle() no pide *parent_category. Y aunque lo pidiera, el componente muestra un solo padre: en las 31 categorías de tercer nivel faltaría la raíz
Ficha del producto Breadcrumb solo para Google, en el JSON-LD (product-jsonld, HU-08) No hay breadcrumb visible. El de Google toma la primera categoría sin sus padres: "Inicio › Fuente ATX", sin "Informática"
Consulta de categorías getCategoryByHandle() y listCategories() en lib/data/categories.ts piden *products Traen todos los productos de la categoría: 6 MB en "notebooks", 5 MB en "celulares". No se usan (los productos de la página salen de Algolia), no entran en la caché de Next (tope de 2 MB) y llenan el build de avisos

Las cuatro fuentes de categorías del proyecto

Fuente Qué es Estado de los datos
Medusa: árbol de categorías (/store/product-categories) Jerarquía con madres e hijas; cada producto se asigna a una 135 categorías: 8 raíces, 96 de segundo nivel, 31 de tercero. En una muestra de 300 productos, todos tienen exactamente una categoría
Payload: catalog-categories (RF-007) Rubros para armar los menús, con imagen y, más adelante, ícono 17 categorías, cargadas a mano, con 2 raíces ("Electrónica" e "Informatica"). La sincronización con el catálogo está pendiente y no guarda el id de la categoría de Medusa
Algolia: campo category Un texto por producto con la categoría más profunda ("Celulares") Plano: no sabe quién es la madre. Lo carga el integrador
El recorrido del visitante Por dónde llegó: menú, búsqueda, enlace externo No es un dato: cambia en cada visita

Criterios de evaluación

  1. Completitud: ¿cubre todo el catálogo hoy?
  2. Coherencia con Google: ¿coincide con el JSON-LD y con páginas que existen, que tienen canónica?
  3. Control del negocio: ¿el negocio puede nombrar y ordenar los niveles?
  4. Esfuerzo: cuánto trabajo pide y de quién.
  5. Dependencias: ¿depende de otro equipo o de un desarrollo que no existe?
  6. Riesgo de desincronización: ¿puede mostrar una ruta que no corresponde al producto?
  7. Rendimiento: consultas extra en la ficha, que ya se arma en el servidor en cada visita.

Propuesta A · Árbol de categorías de Medusa (recomendada)

La ficha ya consulta las categorías del producto a Medusa (*categories en PRODUCT_FIELDS). Con el id de la categoría más profunda se recorre el árbol hacia arriba hasta la raíz, sin depender de la cantidad de niveles (ver cómo obtener la cadena).

Notebooks → su madre es Laptops → su madre es Informática → sin madre, fin
Inicio › Informática › Laptops › Notebooks › Notebook HP 15…

Qué incluye

  • Una función que arma la cadena de una categoría y un componente Breadcrumb compartido.
  • Página de categoría: la cadena completa, terminando en la categoría actual sin enlace.
  • Ficha: la cadena con enlaces a /py/categories/<handle>, y al final el producto sin enlace.
  • JSON-LD de la ficha con la misma cadena, para que Google y la pantalla digan lo mismo.
  • Consulta de categorías: sacar *products de getCategoryByHandle() y listCategories().
  • Casos borde: si el producto tiene varias categorías, la más profunda (el cable HDMI Quanta tiene "Accesorios Pc" y su madre "Informática"). Si no tiene ninguna, "Inicio › Producto".
Criterio Evaluación
Completitud ✅ Todo el catálogo
Coherencia con Google ✅ Cada nivel es una página con canónica propia (RF-011) y el JSON-LD sale del mismo dato
Control del negocio ⚠️ Los nombres y la jerarquía son los del catálogo, que administra el integrador o el panel de Medusa
Esfuerzo ✅ Menos de un día, solo storefront
Dependencias ✅ Ninguna
Riesgo de desincronización ✅ Bajo: la ficha y la categoría leen el mismo dato de Medusa
Rendimiento ✅ Una sola consulta de ~27 KB para todo el sitio, compartida y cacheada. La de la página de categoría baja de varios MB a pocos KB

Cómo obtener la cadena sin importar la cantidad de niveles

Verificado el 2026-09-23 contra el Medusa local. No hay una ruta propia en el backend para esto, pero la Store API de Medusa lo resuelve de fábrica.

Opción Cómo Consultas en la ficha Observaciones
1 · API de categorías de Medusa GET /store/product-categories?handle=<h>&include_ancestors_tree=true&fields=id,name,handle,*parent_category (o /store/product-categories/<id>?…) 1 por categoría, cacheable Sube sola hasta la raíz. Menos de 1 KB. Hay que pedir exactamente *parent_category: con campos sueltos (parent_category.name) la cadena se corta en un padre, sin error. include_ancestors_tree no existe en la API de productos
2 · Árbol completo en memoria (recomendada) listCategoryTree() de lib/data/categories.ts: una lista plana de las 135 categorías (~27 KB, force-cache), y subir por parent_category_id hasta una sin madre 1 para todo el sitio, compartida y casi siempre en caché La función ya existe pero hoy no se usa: la creó el menú lateral de la home (4a4a7f0, 16/09) y quedó sin llamados cuando ese menú se sacó (a22bb31, 18/09). Arma el árbol hacia abajo; para el breadcrumb falta la función que lo recorre hacia arriba
3 · Endpoint propio en el backend Por ejemplo /store/custom/categories/:id/breadcrumb 1 Solo si otro sistema necesita la cadena. Hace lo mismo que la opción 1

Descartadas: campos anidados fijos en la consulta del producto (categories.parent_category.parent_category.*), porque dependen de la profundidad; y buscar por nombre (?name=), porque el nombre no es único.

Probado con categorías de 1, 2 y 3 niveles: "Informática", "Electrónica › Celulares", "Informática › Laptops › Notebooks" y "Electrodomésticos › Línea Blanca › Lavarropas".

Propuesta B · catalog-categories de Payload

El breadcrumb se arma con el árbol que el negocio carga en Payload para los menús.

Criterio Evaluación
Completitud ❌ 17 de 135 categorías. Todo producto de una categoría no cargada quedaría sin breadcrumb
Coherencia con Google ⚠️ Payload no tiene páginas de categoría: los enlaces irían a /py/categories/<slug> suponiendo que el slug de Payload coincide con el handle de Medusa. Si no coincide, da 404
Control del negocio ✅ Total: nombre, orden y agrupación
Esfuerzo ❌ Alto. Además del componente, hay que vincular cada producto a una categoría de Payload, porque los productos no viven ahí. Eso pide la sincronización del RF-007, que no existe
Dependencias ❌ La sincronización de categorías (etapa posterior del RF-007) y la carga completa del árbol
Riesgo de desincronización ❌ Alto. Un árbol a mano sin vínculo con el catálogo se desactualiza, y el error es silencioso: la ruta simplemente no aparece o enlaza mal
Rendimiento ⚠️ Una consulta más a Payload por ficha, cacheable

Por qué no ahora: el árbol de Payload está pensado para el menú, donde agrupar distinto que el catálogo es una virtud ("Regalos accesibles", "Gaming" como mezcla de rubros). Para el breadcrumb es un problema: la ruta tiene que describir dónde está el producto, y eso lo sabe el catálogo.

Propuesta C · Jerarquía en Algolia

El integrador agrega al índice un campo jerárquico por producto, en el formato que usa InstantSearch (categories.lvl0: "Informática", lvl1: "Informática > Laptops", lvl2: "Informática > Laptops > Notebooks").

Criterio Evaluación
Completitud ⚠️ Depende de que el integrador lo cargue en todo el índice
Coherencia con Google ⚠️ Los textos de Algolia no traen el handle de cada nivel: para enlazar hay que reconstruirlo desde el nombre
Control del negocio ⚠️ El del catálogo, igual que en A
Esfuerzo ⚠️ Medio: cambio del integrador más el componente. Como ventaja, habilita un filtro jerárquico en la tienda (HierarchicalMenu)
Dependencias ❌ El integrador
Riesgo de desincronización ❌ Alto. El índice ya está desincronizado de Medusa en los IDs de variante y en las fotos (ver Datos del integrador que rompen la ficha)
Rendimiento ✅ En la tienda viene con cada resultado. En la ficha no sirve: la ficha se arma con Medusa

Cuándo tendría sentido: si se quiere un filtro por niveles en la tienda. Para el breadcrumb no aporta nada que Medusa no tenga ya.

Propuesta D · Medusa para la estructura y Payload para la presentación (evolución)

La jerarquía sale de Medusa, como en A. Cuando exista la sincronización del RF-007, cada categoría de Payload guarda el id de su categoría de Medusa, y el breadcrumb toma de Payload lo que el negocio quiera cambiar —un nombre más amigable, un ícono— y de Medusa todo lo demás.

Criterio Evaluación
Completitud ✅ La de Medusa
Coherencia con Google ✅ La de Medusa
Control del negocio ✅ Sobre la presentación, sin poder romper la estructura
Esfuerzo ⚠️ A, más la lectura de Payload una vez que la sincronización exista
Dependencias ⚠️ La sincronización del RF-007
Riesgo de desincronización ✅ Bajo: el vínculo es por id, y si falta la presentación se usa el nombre de Medusa
Rendimiento ⚠️ Una consulta más a Payload, cacheable

No es una alternativa a A sino su continuación: A deja el componente y la cadena listos, y D les cambia solo de dónde salen los nombres.

Propuesta E · Según por dónde llegó el visitante (descartada)

Mostrar la ruta del menú o de la búsqueda por la que llegó el visitante. Se descarta: la misma ficha mostraría rutas distintas según la visita, Google no puede leerla (llega sin historial) y una ficha abierta desde un enlace externo no tendría ruta. Es útil como botón de "volver a los resultados", no como breadcrumb.

Comparación

A · Medusa B · Payload C · Algolia D · Medusa + Payload E · Recorrido
Completitud ✅ ❌ ⚠️ ✅ ❌
Coherencia con Google ✅ ⚠️ ⚠️ ✅ ❌
Control del negocio ⚠️ ✅ ⚠️ ✅ —
Esfuerzo ✅ Bajo ❌ Alto ⚠️ Medio ⚠️ Medio ⚠️ Medio
Dependencias ✅ ❌ ❌ ⚠️ ✅
Desincronización ✅ ❌ ❌ ✅ —
Rendimiento ✅ ⚠️ ✅ ⚠️ ✅

Recomendación y plan

  1. Ahora, propuesta A, en el storefront:
  2. Corregir la consulta de categorías: fuera *products, dentro los padres.
  3. Función que arma la cadena y componente Breadcrumb compartido.
  4. Página de categoría con la cadena completa.
  5. Ficha con la cadena y el producto al final.
  6. JSON-LD de la ficha con la misma cadena.
  7. Más adelante, propuesta D, cuando el RF-007 sume la sincronización y cada categoría de Payload guarde el id de la de Medusa.
  8. Propuesta C solo si se decide un filtro jerárquico en la tienda.

Decisiones que conviene tomar antes de implementar

# Decisión Recomendación
B1 ¿Qué jerarquía sigue el breadcrumb? Medusa (propuesta A)
B2 Producto con varias categorías: ¿cuál se muestra? La más profunda
B3 ¿El último elemento de la ficha es el nombre del producto o de la variante? El del producto: la canónica apunta al producto base (RF-011)
B4 ¿Se muestra en celular? Sí, recortando los niveles intermedios si no entra: "Inicio › … › Notebooks"
B5 ¿Los nombres de las categorías se muestran tal como vienen de Medusa? Sí por ahora. Algunos traen mayúsculas o signos del integrador ("ANTISHOCK", "Accesorios p/ Notebook."); corregirlos es trabajo en el catálogo o parte de la propuesta D