Respuesta corta: sí, puedes añadir una wishlist a Shopify sin una app. No, Shopify no la incluye de forma nativa. Y no, la mayoría de los métodos DIY no sobrevivirán a una tienda real a escala — pero un par funcionan bien si tienes menos de unos cientos de productos y un tema amigable para desarrolladores.
Esta guía te da todos los métodos que funcionan, el código real y una valoración honesta sobre dónde falla cada uno. Al terminar sabrás si lanzar la versión DIY o ahorrarte el quebradero de cabeza.
¿Tiene Shopify una wishlist integrada?
No. A fecha de 2026, Shopify sigue sin incluir una función de wishlist en ningún plan — ni Basic, ni Plus. O la construyes tú mismo o instalas una app de terceros.
Por eso existe esta búsqueda. Decenas de miles de comerciantes buscan “shopify wishlist sin app” cada mes esperando encontrar un ajuste oculto. No lo hay. Pero hay cuatro formas razonables de construir una con Liquid, JavaScript y metafields — vamos a verlas.
Método 1: wishlist con localStorage (el DIY más común)
Este es el método que el 90% de los tutoriales de “wishlist gratis” enseñan. Añades un botón con un corazón a tus tarjetas de producto, guardas los datos del producto en el localStorage del navegador y renderizas una página de wishlist a partir de ese array almacenado.
Pros: No requiere login. Funciona offline. Cero backend. ~50 líneas de código.
Contras: La wishlist vive en un navegador, en un dispositivo. Si una persona guarda algo en móvil y vuelve desde el escritorio, su wishlist está vacía. ¿Borra las cookies? Adiós. ¿Cambia de navegador? Adiós. Tampoco puedes enviarle un correo cuando un artículo baja de precio o vuelve al stock — no tienes ni idea de quién es.
Paso 1 — Añade un botón de wishlist a tu página de producto
Abre el editor de código de tu tema (Tienda online → Temas → Editar código) y busca el snippet de plantilla del producto (normalmente snippets/product-card.liquid o dentro de sections/main-product.liquid). Coloca esto donde quieras el corazón:
<button
type="button"
class="wishlist-btn"
data-id="{{ product.id }}"
data-handle="{{ product.handle }}"
data-title="{{ product.title | escape }}"
data-image="{{ product.featured_image | image_url: width: 400 }}"
data-price="{{ product.price | money }}"
aria-label="Add to wishlist">
<span class="wishlist-icon">♡</span>
<span class="wishlist-label">Save</span>
</button>
Paso 2 — Conecta el JavaScript
Añade esto en assets/wishlist.js (crea el archivo) e inclúyelo desde theme.liquid con {{ 'wishlist.js' | asset_url | script_tag }}:
(function () {
const KEY = 'shopify_wishlist_v1';
const read = () => JSON.parse(localStorage.getItem(KEY) || '[]');
const write = (list) => localStorage.setItem(KEY, JSON.stringify(list));
function syncButton(btn, list) {
const inList = list.some(i => i.id === btn.dataset.id);
btn.classList.toggle('in-wishlist', inList);
btn.querySelector('.wishlist-icon').textContent = inList ? '♥' : '♡';
btn.querySelector('.wishlist-label').textContent = inList ? 'Saved' : 'Save';
}
function updateCount() {
const count = read().length;
document.querySelectorAll('.wishlist-count').forEach(el => {
el.textContent = count;
el.hidden = count === 0;
});
}
document.querySelectorAll('.wishlist-btn').forEach(btn => {
syncButton(btn, read());
btn.addEventListener('click', () => {
let list = read();
const exists = list.find(i => i.id === btn.dataset.id);
if (exists) {
list = list.filter(i => i.id !== btn.dataset.id);
} else {
list.push({
id: btn.dataset.id,
handle: btn.dataset.handle,
title: btn.dataset.title,
image: btn.dataset.image,
price: btn.dataset.price,
addedAt: Date.now()
});
}
write(list);
syncButton(btn, list);
updateCount();
});
});
updateCount();
})();
Paso 3 — Construye la página de wishlist
Crea una nueva página en Tienda online → Páginas llamada “Wishlist” con el handle wishlist. Después crea una plantilla templates/page.wishlist.liquid y asígnala a esa página:
{% layout 'theme' %}
<div class="page-width wishlist-page">
<h1>Your wishlist</h1>
<div id="wishlist-grid" class="wishlist-grid"></div>
<p id="wishlist-empty" hidden>
Your wishlist is empty.
<a href="{{ routes.collections_url }}/all">Browse products</a>.
</p>
</div>
<script>
(function () {
const list = JSON.parse(localStorage.getItem('shopify_wishlist_v1') || '[]');
const grid = document.getElementById('wishlist-grid');
const empty = document.getElementById('wishlist-empty');
if (!list.length) { empty.hidden = false; return; }
grid.innerHTML = list.map(item => `
<article class="wishlist-card">
<a href="/products/${item.handle}">
<img src="${item.image}" alt="${item.title}" loading="lazy">
<h3>${item.title}</h3>
<p>${item.price}</p>
</a>
<button data-id="${item.id}" class="wishlist-remove">Remove</button>
</article>
`).join('');
grid.addEventListener('click', e => {
const btn = e.target.closest('.wishlist-remove');
if (!btn) return;
const next = JSON.parse(localStorage.getItem('shopify_wishlist_v1'))
.filter(i => i.id !== btn.dataset.id);
localStorage.setItem('shopify_wishlist_v1', JSON.stringify(next));
btn.closest('.wishlist-card').remove();
if (!next.length) { grid.innerHTML = ''; empty.hidden = false; }
});
})();
</script>
Esa es la wishlist completa con localStorage. Funciona. Y también tiene un 30-50% de abandono entre compradores multidispositivo, ninguna analítica y no puedes enviar correo a nadie cuando algo que querían se rebaja o vuelve al stock. Lo que nos lleva al método 2.
Método 2: metafields de cliente (el enfoque DIY “como Dios manda”)
Si tus compradores crean cuentas, puedes almacenar la wishlist en el objeto cliente mediante metafields. Ahora la wishlist les sigue entre dispositivos y técnicamente puedes enviarles correos — aunque enviar esos correos es un problema aparte.
Pros: Sincronización entre dispositivos. Datos reales que puedes consultar. Sobrevive al borrado del navegador.
Contras: Requiere que el cliente inicie sesión (caída inmediata del 60–80% frente al modo anónimo). Escribir en metafields desde el storefront requiere la Storefront API con un token de acceso de cliente, o un App Proxy + una pequeña función serverless. No hay solución copiar-y-pegar — te comprometes a desarrollo.
Configura el metafield
Ve a Configuración → Datos personalizados → Clientes → Añadir definición:
- Namespace y key:
custom.wishlist - Tipo:
JSON - Acceso: lectura + escritura desde Storefront
Muestra el estado de la wishlist en Liquid
{% if customer %}
{% assign wishlist_raw = customer.metafields.custom.wishlist.value | default: '[]' %}
{% assign wishlist = wishlist_raw | parse_json %}
<button
class="wishlist-btn-meta"
data-id="{{ product.id }}"
data-customer="{{ customer.id }}">
{% if wishlist contains product.id %}♥ Saved{% else %}♡ Save{% endif %}
</button>
{% else %}
<a href="{{ routes.account_login_url }}?return_url={{ request.path }}"
class="wishlist-btn-logged-out">♡ Log in to save</a>
{% endif %}
Escribe en el metafield
Aquí es donde la cosa se complica. La Storefront API no te deja mutar customer.metafields directamente desde JS anónimo — necesitas un token de acceso de cliente, o un App Proxy autenticado. Un handler mínimo de App Proxy (Node.js, desplegado en Vercel/Cloudflare Workers) se ve así:
// /api/wishlist-toggle (Shopify App Proxy)
export default async function handler(req) {
const { customerId, productId } = await req.json();
const query = `
mutation updateWishlist($id: ID!, $value: String!) {
customerUpdate(input: {
id: $id,
metafields: [{
namespace: "custom",
key: "wishlist",
type: "json",
value: $value
}]
}) {
customer { id }
userErrors { message }
}
}
`;
// 1. Fetch current list
// 2. Toggle productId
// 3. Send mutation to Admin API
// 4. Return new state
}
La implementación completa son unas 200 líneas una vez que manejas autenticación, estados de error y limitación de tasa. Tiempo realista de desarrollo para un programador que no lo ha hecho antes: 1–2 días. Para uno que sí: 2–3 horas.
Método 3: carrito oculto con line item properties (guardar para después)
Algunos tutoriales sugieren abusar del carrito añadiendo artículos de la wishlist con cantidad 0 o con una propiedad personalizada como _wishlist: true. No lo hagas. Rompe los cálculos del carrito, confunde al checkout y la mayoría de temas renderizarán los artículos de todos modos. Lo incluyo solo para que sepas evitarlo cuando lo veas sugerido en Reddit.
La única variante medianamente defendible: un atributo de carrito “Guardado para después” separado, que renderizas fuera del carrito principal, así:
<!-- in cart.liquid -->
{% assign saved = cart.attributes.saved_items | split: ',' %}
{% if saved.size > 0 %}
<div class="saved-for-later">
<h2>Saved for later</h2>
{% for handle in saved %}
{% assign p = all_products[handle] %}
<a href="/products/{{ p.handle }}">{{ p.title }} — {{ p.price | money }}</a>
{% endfor %}
</div>
{% endif %}
Es una solución a medias. Los atributos del carrito desaparecen cuando se vacía. Sáltatelo.
Método 4: wishlist con URL para compartir (el truco social)
Este no es una wishlist real — es un enlace de “comparte tus favoritos”. Construyes una lista en el cliente y la codificas en una URL que el comprador puede compartir o guardar en favoritos.
// Build a share URL from the localStorage list
function getShareUrl() {
const ids = JSON.parse(localStorage.getItem('shopify_wishlist_v1') || '[]')
.map(i => i.id)
.join(',');
return `${location.origin}/pages/wishlist?items=${ids}`;
}
// On the wishlist page, hydrate from ?items=
const params = new URLSearchParams(location.search);
if (params.get('items')) {
const ids = params.get('items').split(',');
// Fetch each /products/{id}.js and render
Promise.all(ids.map(id =>
fetch(`/products/${id}.js`).then(r => r.json())
)).then(renderWishlist);
}
Útil como complemento del método 1 — da a los compradores una forma de compartir listas con amigos o enviárselas a sí mismos. No es una wishlist por sí sola.
El veredicto honesto: cuándo el DIY vale y cuándo se rompe
El DIY funciona si puedes responder sí a las cuatro:
- Tu tienda tiene menos de ~500 SKUs (para que el mantenimiento manual sea soportable).
- No te importa la sincronización entre dispositivos (o estás forzando cuentas de cliente de todas formas).
- No planeas enviar correos a clientes cuando los artículos de la wishlist se rebajen o vuelvan al stock.
- Tienes un desarrollador en plantilla o te sientes cómodo editando Liquid tú mismo.
El DIY se rompe en el momento en que quieres:
- Enviar un correo a un cliente diciendo “tu artículo de la wishlist ha vuelto al stock” — no tienes ni idea de a quién pertenece la entrada de localStorage.
- Recuperar wishlists abandonadas con SMS o correo — mismo problema.
- Mostrar la conversión de la wishlist en analítica — puedes disparar eventos a GA4, pero no podrás vincularlos a ingresos sin trabajo de backend.
- Permitir que invitados guarden sin obligarles a crear cuenta, y sincronizar con su correo cuando inicien sesión más tarde.
- Entregársela a un marketer no técnico para que la gestione.
No son limitaciones teóricas. Son los motivos por los que la mayoría de tiendas que empiezan con una wishlist DIY cambian a una app en 6 meses.
Lo que cuesta realmente una wishlist DIY (siendo honestos)
La gente busca “sin una app” porque asume que la app es la opción cara. Vamos a comprobarlo.
Un desarrollador construyendo la versión con localStorage: 2–4 horas. A 75 $/h, son 150–300 $, sin coste recurrente. Razonable.
Un desarrollador construyendo la versión con metafields y disparadores de correo: 8–16 horas. 600–1.200 $, más un Klaviyo o similar para enviar realmente los correos (20–45 $/mes). Más el mantenimiento cada vez que tu tema se actualiza o Shopify cambia sus extensiones de checkout.
Una app de wishlist + una app de back-in-stock + una app de preorder, por separado: típicamente 15–25 $/mes cada una. O sea, 45–75 $/mes por las tres funciones.
Una app combinada: una sola factura.
O: consigue las tres en una app, con plan gratuito
Aviso — nosotros hacemos Notify Me!, y la construimos específicamente porque la mayoría de tiendas necesitan wishlist + back-in-stock + preorder juntas. El cliente guarda un producto → se queda sin stock → recibe una notificación → si no vuelve, le ofreces un preorder. Tres funciones que pertenecen al mismo flujo.
Cómo es realmente la configuración
Activas funciones por página (producto, colección, inicio) desde el panel — sin código de tema que mantener:
La pantalla de Wishlist en Notify Me! — el modo invitado está activado por defecto, y la wishlist se vincula a la cuenta del cliente en el momento en que inicia sesión. Sin cambios de código.
Botón de página de producto — totalmente personalizable
Elige un icono de corazón, marcador o estrella. Edita las etiquetas de ambos estados. El widget se renderiza en línea junto a tu botón de Añadir al carrito:
Ajustes del botón en la página de producto — define las etiquetas antes/después, el icono y el estilo sin tocar Liquid.
Widget de página de colección — funciona en todas las tarjetas
Donde más sufren los métodos DIY: poner un botón funcional para guardar en cada tarjeta de la página de colección. La app lo gestiona con un solo interruptor:
Widget de página de colección — elige icono, posición y estilo de visualización. Se renderiza en cada tarjeta de producto automáticamente.
Precios (facturación anual)
| Plan | Precio | Acciones de wishlist | Notificaciones back-in-stock | Preorders |
|---|---|---|---|---|
| Lite | Gratis | 50 de por vida | 100 de por vida | 5 de por vida |
| Starter | 15,92 $/mes | 24.000/año | 6.000/año | 6.000/año |
| Standard | 31,92 $/mes | 120.000/año | 18.000/año | 18.000/año |
| Rocket | 55,92 $/mes | 300.000/año | 60.000/año | 120.000/año |
| Plus | 399,92 $/mes | Ilimitadas | Ilimitadas | Ilimitados |
Todos los detalles del plan en la página de precios. El plan Lite gratuito existe para que puedas probarlo de verdad en tu tienda antes de pagar nada. La mayoría de tiendas se quedan en Starter a largo plazo — 15,92 $/mes reemplaza tres apps que de otro modo instalarías por separado.
Funciones que las versiones DIY de arriba no pueden igualar:
- Modo invitado (sin login forzado) — la wishlist se vincula automáticamente a la cuenta al primer inicio de sesión
- Múltiples listas con nombre por cliente
- Widgets de wishlist en páginas de inicio, colección y producto
- Disparadores automáticos de correo y SMS en bajada de precio, stock bajo y reposición
- Botones de preorder integrados y pagos parciales
- Personalización completa con Liquid para combinar con tu tema
- Soporte B2B y DTC listo para usar
Empieza gratis en la Shopify App Store →
Preguntas frecuentes
¿Se puede añadir una wishlist a Shopify sin programar?
No sin una app. Cada método sin código requiere instalar una app de wishlist o pegar código en tu tema. No existe un interruptor nativo.
¿Hay alguna wishlist gratis para Shopify?
Sí, algunas. El método con localStorage de arriba es gratis si lo construyes tú mismo. En cuanto a apps, el plan Lite de Notify Me! es gratis hasta 50 acciones de wishlist de por vida, lo que basta para validar que los clientes realmente usan la función en tu tienda antes de pagar por un plan superior.
¿Shopify 2.0 tiene wishlist?
No. Shopify 2.0 es la arquitectura del tema (sections, blocks, app blocks) — no añade una función de wishlist. Sí que hace más limpia la instalación de una app de wishlist a través de app blocks, por lo que la mayoría de apps de wishlist modernas se integran en temas 2.0 en segundos.
¿Puedo añadir una wishlist solo con Liquid?
Parcialmente. Liquid puede mostrar una wishlist si los datos están en un metafield de cliente, pero Liquid no puede escribir en metafields desde el storefront. Necesitarás JavaScript y o bien la Storefront API o un App Proxy para guardar artículos.
¿Cómo añado una wishlist a una página de producto de Shopify sin una app?
Usa el Método 1 de arriba: pega el snippet del botón de wishlist en tu plantilla de producto, añade wishlist.js a los assets de tu tema y crea una página de wishlist desde templates/page.wishlist.liquid. Todo el código está en esta guía.
¿De verdad una wishlist aumenta las conversiones?
Sí — guardar señala intención de compra real, y los compradores que usan wishlist suelen convertir a tasas significativamente más altas que los que solo navegan. El mayor empujón viene de enviar correos a quienes tienen artículos en wishlist cuando algo se rebaja o vuelve al stock — ahí es donde los métodos DIY se quedan cortos.
¿Qué pasa con una wishlist en localStorage cuando el cliente borra las cookies?
Se elimina permanentemente. Sin copia de seguridad, sin recuperación. Esta es la razón número uno por la que las tiendas superan el enfoque DIY.
¿Una wishlist puede disparar correos de back-in-stock?
Solo si vinculas la wishlist a un correo de cliente. localStorage no puede. Los metafields de cliente sí, pero aún necesitas un backend para detectar cambios de inventario y enviar el correo. Una app que hace ambas — como Notify Me! — se ahorra todo ese trabajo.
¿Cuál es la diferencia entre wishlist y waitlist?
Una wishlist es una lista curada de productos que un comprador quiere recordar; una waitlist es una cola para un producto que está temporalmente sin stock o aún por lanzarse. Las wishlists son abiertas; las waitlists están limitadas por el inventario o la fecha de lanzamiento. La mayoría de tiendas acaban queriendo ambas.
¿Es mejor usar Continuar vendiendo sin stock o una wishlist?
Problema distinto. Continuar vendiendo sin stock te permite aceptar pedidos de inventario no disponible (esencialmente un preorder). Una wishlist permite a los compradores guardar artículos sin comprometerse. Si intentas capturar demanda sobre un producto no disponible, normalmente quieres ambas — una wishlist para reunir intención y una opción de preorder para los compradores listos para comprar ya.
Última actualización: junio de 2026. Escrito para temas de Shopify en Online Store 2.0. Si tu tema es más antiguo (vintage), las rutas de Liquid varían ligeramente — el JavaScript es idéntico.
