> ## Documentation Index
> Fetch the complete documentation index at: https://docs.lantern.is/llms.txt
> Use this file to discover all available pages before exploring further.

# Custom Wishlist Implementation

> Learn how to build custom wishlist functionality using Lantern's JavaScript API and metafields

For developers who want to build custom wishlist experiences beyond Lantern's built-in extensions, this guide shows you how to implement wishlist functionality using Lantern's JavaScript API and metafields.

## Prerequisites

Before building custom wishlist functionality, ensure you have:

* Wishlists enabled in your Lantern app
* Basic knowledge of JavaScript and Liquid templating
* Access to your theme's code

## Accessing Wishlist Data

### Customer Metafields

Lantern stores wishlist data in customer metafields that you can access in your Liquid templates

* `customer.metafields.lantern.wishlist` - Array of product variant references in the customer's wishlist

### JS API

Lantern also stores the wishlist data and functionality in the `window.lantern` object. You can access it using the following methods:

* `window.lantern.wishlist` - Array of product variant references in the customer's wishlist
* `window.lantern.api.toggleListItem` - Function to add or remove a product variant from the wishlist

## Adding a Custom "Add to Wishlist" Button

To create your own button to add or remove a product variant from the wishlist, add the following code snippet to your theme's Liquid template:

```liquid theme={null}
<button
  type="button"
  class="wishlist-toggle"
  data-product-variant-id="{{ product.selected_or_first_available_variant.id }}"
  onclick="toggleWishlist(this)"
>
  <span class="wishlist-add-text">Add to Wishlist</span>
  <span class="wishlist-remove-text">Remove from Wishlist</span>
</button>

<script>
  async function toggleWishlist(button) {
    // Check if user is logged in
    if (!window.lantern?.customer?.id) {
      alert('Please log in to add items to your wishlist.');
      return;
    }

    // Check if Lantern API is available
    if (!window.lantern?.api?.toggleListItem) {
      console.error('Lantern API not available');
      return;
    }

    const variantId = button.dataset.productVariantId;

    // Disable button during API call
    button.disabled = true;

    try {
      // Call the API and get the updated wishlist back
      const updatedWishlist = await window.lantern.api.toggleListItem({
        productVariantId: variantId
      });

      // Update button state based on the returned wishlist
      updateWishlistButton(button, updatedWishlist);

    } catch (error) {
      console.error('Error toggling wishlist item:', error);
      alert('There was an error updating your wishlist. Please try again.');
    } finally {
      button.disabled = false;
    }
  }

  function updateWishlistButton(button, wishlist = null) {
    const variantId = button.dataset.productVariantId;

    // Use provided wishlist or fall back to window.lantern.customer.wishlist
    const currentWishlist = wishlist || window.lantern?.customer?.wishlist || [];

    // Check if this variant is in the wishlist
    const isInWishlist = currentWishlist.some(item => item.id === variantId);

    // Update button appearance
    if (isInWishlist) {
      button.classList.add('is-favorited');
    } else {
      button.classList.remove('is-favorited');
    }
  }

  // Initialize button state when Lantern is ready
  function initWishlistButtons() {
    const buttons = document.querySelectorAll('.wishlist-toggle');
    if (buttons.length > 0 && window.lantern?.customer?.wishlist) {
      buttons.forEach(button => updateWishlistButton(button));
    }
  }

  // Initialize when DOM is ready
  if (document.readyState === 'loading') {
    document.addEventListener('DOMContentLoaded', initWishlistButtons);
  } else {
    initWishlistButtons();
  }

  // Also initialize when Lantern loads (if it's not ready yet)
  if (!window.lantern?.customer) {
    let attempts = 0;
    const maxAttempts = 50; // Poll for 5 seconds
    const checkLantern = setInterval(() => {
      attempts++;
      if (window.lantern?.customer || attempts >= maxAttempts) {
        clearInterval(checkLantern);
        // If customer object is now available, initialize buttons
        if (window.lantern?.customer) {
          initWishlistButtons();
        }
      }
    }, 100);
  }
</script>

<style>
  .wishlist-toggle {
    padding: 10px 16px;
    border: 1px solid #ccc;
    background: white;
    cursor: pointer;
    border-radius: 4px;
  }

  .wishlist-toggle:disabled {
    opacity: 0.6;
    cursor: not-allowed;
  }

  .wishlist-toggle.is-favorited {
    background: #f0f0f0;
    border-color: #999;
  }

  .wishlist-remove-text {
    display: none;
  }

  .wishlist-remove-text,
  .wishlist-toggle.is-favorited .wishlist-add-text {
    display: none;
  }

  .wishlist-toggle.is-favorited .wishlist-remove-text {
    display: inline;
  }
</style>
```

## Rendering Wishlist Items

### Creating a Wishlist Section

Create a custom section template (`sections/wishlist.liquid`) to display saved items using your themes built in product grid snippet (`card-product` for a Dawn based theme, like in this example).

```liquid theme={null}
<div class="collection-list-wrapper isolate section-{{ section.id }}-padding">
  <h2 class="wishlist__heading {{ section.settings.heading_size }}">
    {{ section.settings.title | escape }}
  </h2>
  {% if customer %}
    {% assign wishlist = customer.metafields.lantern.wishlist.value %}
    {% if wishlist and wishlist.size > 0 %}
      <ul
        class="grid product-grid grid--4-col-desktop grid--2-col-tablet-down"
        role="list"
      >
        {% for variant in wishlist %}
          {% assign product = variant.product %}
          <li class="grid__item">
            {% render 'card-product',
              card_product: product,
              variant: variant,
              media_aspect_ratio: section.settings.image_ratio,
              show_secondary_image: section.settings.show_secondary_image,
              show_vendor: true
            %}
          </li>
        {% endfor %}
      </ul>
    {% else %}
      <p>Your wishlist is empty.</p>
    {% endif %}
  {% else %}
    <div class="isolate page-width">
      <div class="rich-text content-container section-{{ section.id }}-padding">
        <div class="rich-text__wrapper rich-text__wrapper--{{ section.settings.desktop_content_position }}{% unless section.settings.full_width %} page-width{% endunless %}">
          <p class="wishlist-login"><a href="{{ routes.account_login_url }}">Log in</a> to view your wishlist.</p>
        </div>
      </div>
    </div>
  {% endif %}
</div>

{% schema %}
{
  "name": "Wishlist",
  "tag": "section",
  "class": "section",
  "disabled_on": {
    "groups": ["header", "footer"]
  },
  "settings": [
    {
      "type": "text",
      "id": "title",
      "default": "Wishlist",
      "label": "Title"
    },
    {
      "type": "select",
      "id": "heading_size",
      "options": [
        {
          "value": "h2",
          "label": "t:sections.all.heading_size.options__1.label"
        },
        {
          "value": "h1",
          "label": "t:sections.all.heading_size.options__2.label"
        },
        {
          "value": "h0",
          "label": "t:sections.all.heading_size.options__3.label"
        }
      ],
      "default": "h1",
      "label": "t:sections.all.heading_size.label"
    },
    {
      "type": "header",
      "content": "t:sections.all.padding.section_padding_heading"
      }
  ]
}
{% endschema %}
```
