Wishlist Page
Build a dedicated wishlist page with product grid, remove, and add-to-cart using the Swym JS SDK.
Wishlist Page
A dedicated page showing all wishlisted products with remove and add-to-cart actions.
Setup
Step 1 — Create a page template
In your theme code editor (Online Store > Themes > Edit code):
- OS 2.0 (JSON templates): Create
templates/page.wishlist.jsonthat references a new section:
{
"sections": {
"main": {
"type": "swym-wishlist-page",
"settings": {}
}
},
"order": ["main"]
}Then create sections/swym-wishlist-page.liquid with the Liquid below.
- Liquid templates: Create
templates/page.wishlist.liquiddirectly with the Liquid below.
Step 2 — Create a Shopify page
In your Shopify admin: Online Store > Pages > Add page. Set the title to "Wishlist". In the Theme template dropdown (right sidebar), select page.wishlist.
Step 3 — Add the JavaScript asset
Create assets/swym-wishlist-page.js in your theme with the JavaScript below.
Section Liquid — sections/swym-wishlist-page.liquid
sections/swym-wishlist-page.liquid{% comment %} Swym Wishlist Page — SDK-Only Mode {% endcomment %}
<div id="swym-wishlist-page" class="page-width">
<h1>My Wishlist <span id="swym-wishlist-count"></span></h1>
{%- comment -%} Login nudge for anonymous users {%- endcomment -%}
<div id="swym-login-nudge" style="display:none;">
<p>Sign in to save your wishlist across devices</p>
<a href="/account/login?return_url=/pages/wishlist">Sign in</a>
</div>
{%- comment -%} Save nudge — shown after 3+ items for anonymous users {%- endcomment -%}
<div id="swym-save-nudge" style="display:none;">
<p><strong>Don't lose your list!</strong> You've saved <span id="swym-save-nudge-count"></span> items. <a href="/account/login?return_url=/pages/wishlist">Sign in to keep them.</a></p>
<button id="swym-save-nudge-dismiss" aria-label="Dismiss">×</button>
</div>
<div id="swym-wishlist-loading">Loading your wishlist...</div>
<div id="swym-wishlist-empty" style="display:none;">
<p>Your wishlist is empty</p>
<p>Start adding items you love</p>
<a href="/collections">Browse products</a>
</div>
<div id="swym-wishlist-grid"></div>
</div>
<script src="{{ 'swym-wishlist-page.js' | asset_url }}" defer></script>Adapting Cards to Your Theme
The wishlist page product cards must match your collection page's card layout. A shopper should not be able to tell the wishlist page was built separately.
Before writing the JavaScript, read your collection section file and identify:
- Image wrapper — aspect ratio,
object-fit, CSS classes - Title element — HTML tag, class, font styling
- Price element — format, class, currency symbol
- Card wrapper — classes, spacing
- Grid —
grid-template-columns,gap, responsive breakpoints
Then replicate that structure in the renderProducts function below. The default implementation uses generic .swym-wishlist-card classes — replace these with your theme's actual card classes.
JavaScript — assets/swym-wishlist-page.js
assets/swym-wishlist-page.js(function() {
'use strict';
var SAVE_NUDGE_THRESHOLD = 3;
function initWishlistPage(swat) {
// Use the SDK's built-in login check — this is the canonical way.
// Under the hood, Swym's Liquid snippet sets window.swymCustomerId
// from Shopify's {{ customer.id }} object. The SDK reads it on init.
var isLoggedIn = swat.platform.isLoggedIn();
var grid = document.getElementById('swym-wishlist-grid');
var loading = document.getElementById('swym-wishlist-loading');
var empty = document.getElementById('swym-wishlist-empty');
var countEl = document.getElementById('swym-wishlist-count');
if (!grid) return;
// Show login nudge for anonymous users
if (!isLoggedIn) {
var loginNudge = document.getElementById('swym-login-nudge');
if (loginNudge) loginNudge.style.display = '';
}
swat.fetch(function(products) {
if (loading) loading.style.display = 'none';
if (!products || products.length === 0) {
if (empty) empty.style.display = 'block';
if (countEl) countEl.textContent = '(0 items)';
return;
}
if (countEl) countEl.textContent = '(' + products.length + ' items)';
showSaveNudge(swat, products.length);
renderProducts(swat, products, grid, countEl, empty);
});
// Re-render on wishlist changes
if (swat.evtLayer) {
swat.evtLayer.addEventListener('sw:removedfromwishlist', function() {
swat.fetch(function(products) {
renderProducts(swat, products, grid, countEl, empty);
if (countEl) countEl.textContent = '(' + (products ? products.length : 0) + ' items)';
});
});
}
}
function showSaveNudge(swat, count) {
if (swat.platform.isLoggedIn()) return;
if (count < SAVE_NUDGE_THRESHOLD) return;
if (sessionStorage.getItem('swym-save-nudge-dismissed')) return;
var nudge = document.getElementById('swym-save-nudge');
if (!nudge) return;
var countSpan = document.getElementById('swym-save-nudge-count');
if (countSpan) countSpan.textContent = count;
nudge.style.display = '';
var dismiss = document.getElementById('swym-save-nudge-dismiss');
if (dismiss) {
dismiss.addEventListener('click', function() {
nudge.style.display = 'none';
sessionStorage.setItem('swym-save-nudge-dismissed', 'true');
});
}
}
function renderProducts(swat, products, container, countEl, emptyEl) {
container.innerHTML = '';
if (!products || products.length === 0) {
if (emptyEl) emptyEl.style.display = 'block';
return;
}
if (emptyEl) emptyEl.style.display = 'none';
// IMPORTANT: Replace the HTML below with your theme's collection card structure
// to match the look and feel of your collection page
products.forEach(function(product) {
var card = document.createElement('div');
card.className = 'swym-wishlist-card';
// Product image
var imgLink = document.createElement('a');
imgLink.href = product.du || '#';
var img = document.createElement('img');
img.src = product.iu || '';
img.alt = product.dt || 'Product';
img.loading = 'lazy';
imgLink.appendChild(img);
card.appendChild(imgLink);
// Product title
var titleLink = document.createElement('a');
titleLink.href = product.du || '#';
titleLink.textContent = product.dt || 'Product';
card.appendChild(titleLink);
// Price
if (product.pr) {
var priceEl = document.createElement('span');
priceEl.textContent = formatPrice(product.pr);
card.appendChild(priceEl);
}
// Actions container
var actions = document.createElement('div');
actions.className = 'swym-wishlist-card-actions';
// Add to Cart
var inStock = product.stk !== 0;
var cartBtn = document.createElement('button');
cartBtn.type = 'button';
cartBtn.textContent = inStock ? 'Add to Cart' : 'Out of Stock';
cartBtn.disabled = !inStock;
if (inStock) {
cartBtn.addEventListener('click', function() {
cartBtn.textContent = 'Adding...';
cartBtn.disabled = true;
fetch('/cart/add.js', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ items: [{ id: Number(product.epi), quantity: 1 }] })
})
.then(function(response) {
if (response.ok) {
cartBtn.textContent = 'Added!';
setTimeout(function() {
cartBtn.textContent = 'Add to Cart';
cartBtn.disabled = false;
}, 2000);
} else {
cartBtn.textContent = 'Error';
cartBtn.disabled = false;
}
})
.catch(function() {
cartBtn.textContent = 'Error';
cartBtn.disabled = false;
});
});
}
actions.appendChild(cartBtn);
// Remove
var removeBtn = document.createElement('button');
removeBtn.type = 'button';
removeBtn.textContent = '\u00d7';
removeBtn.setAttribute('aria-label', 'Remove from wishlist');
removeBtn.addEventListener('click', function() {
swat.removeFromWishList(
{ epi: product.epi, empi: product.empi, du: product.du },
function() {
card.remove();
var remaining = container.querySelectorAll('.swym-wishlist-card');
if (countEl) countEl.textContent = '(' + remaining.length + ' items)';
if (remaining.length === 0 && emptyEl) {
emptyEl.style.display = 'block';
}
}
);
});
actions.appendChild(removeBtn);
card.appendChild(actions);
container.appendChild(card);
});
}
function formatPrice(priceInCents) {
return '$' + (priceInCents / 100).toFixed(2);
}
window.SwymCallbacks = window.SwymCallbacks || [];
window.SwymCallbacks.push(initWishlistPage);
})();How It Works
- On SDK ready, checks login state via
swat.platform.isLoggedIn()— shows login nudge for anonymous users swat.fetch()gets all wishlisted products- If empty → shows empty state with browse CTA. If products exist → renders cards
- Save nudge: If anonymous user has 3+ items, shows "Don't lose your list" prompt (once per session, dismissible)
- Remove calls
swat.removeFromWishList(), removes the card from DOM, updates count - Add to Cart POSTs to Shopify's
/cart/add.jswith the variant ID, shows "Added!" feedback - Listens for
sw:removedfromwishlistevents to re-render in real-time
Conversion Features
| Feature | When | What |
|---|---|---|
| Login nudge | Anonymous user visits wishlist page | Soft banner: "Sign in to save your wishlist across devices" |
| Save nudge | Anonymous user has 3+ items | "Don't lose your list! You've saved N items. Sign in to keep them." |
| Empty state | 0 items | "Start adding items you love" with browse CTA |
| Return URL | Login link | Always passes ?return_url=/pages/wishlist so user returns after login |
Why 3 items? This threshold balances intent signals — users with 3+ items have demonstrated enough engagement that a save prompt feels helpful rather than intrusive.
Product Fields Used
| Field | Usage |
|---|---|
iu | Product image |
dt | Product title |
du | Product URL (links) |
pr | Price in cents (÷ 100 for display). Use your store's money format for non-USD |
epi | Variant ID (for cart) |
empi | Product ID |
Styling
The page renders semantic HTML — style it to match your theme's collection page:
| Element | Purpose |
|---|---|
#swym-wishlist-grid | Product grid container — match your collection grid's grid-template-columns, gap |
.swym-wishlist-card | Product card — replace with your theme's card classes |
#swym-login-nudge | Login banner — style as a soft info banner |
#swym-save-nudge | Save prompt — style as an attention/warning banner |
#swym-wishlist-empty | Empty state — center-aligned with browse CTA |
Critical: The product cards should reuse your theme's collection page card structure (same image ratio, typography, price format, grid columns). A shopper should not be able to tell this page was built separately from the collection page.
Updated about 7 hours ago