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¶
- Completitud: ¿cubre todo el catálogo hoy?
- Coherencia con Google: ¿coincide con el JSON-LD y con páginas que existen, que tienen canónica?
- Control del negocio: ¿el negocio puede nombrar y ordenar los niveles?
- Esfuerzo: cuánto trabajo pide y de quién.
- Dependencias: ¿depende de otro equipo o de un desarrollo que no existe?
- Riesgo de desincronización: ¿puede mostrar una ruta que no corresponde al producto?
- 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
Breadcrumbcompartido. - 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
*productsdegetCategoryByHandle()ylistCategories(). - 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¶
- Ahora, propuesta A, en el storefront:
- Corregir la consulta de categorías: fuera
*products, dentro los padres. - Función que arma la cadena y componente
Breadcrumbcompartido. - Página de categoría con la cadena completa.
- Ficha con la cadena y el producto al final.
- JSON-LD de la ficha con la misma cadena.
- 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.
- 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 |