Wishlist
Let shoppers save products for later and come back to buy them.
A heart on every product card and product page. Saved products appear on a Wishlist page, in a My Account tab and in the Moduly Wishlist Divi module, where shoppers change quantities, add them to the cart or share the list with a private link. A variable product keeps the variation the shopper chose. Guests can save too: their list is kept in the browser and moves into their account when they log in. Saved hearts stay correct on cached pages.
Extension · needs WooCommerce
Requirements
- WordPress 6.2 or newer
- PHP 7.4 or newer
- WooCommerce 7.0 or newer
Configure it
- Switch it on in Moduly → Installed Widgets.
- Hearts appear at once on shop and category pages, on Moduly product grids and carousels, in quick view and on product pages.
- A Wishlist page is created for you the next time you open the admin, and a Wishlist tab is added to My Account.
- Click Manage on Wishlist to change where the buttons go, their text and colour, the table's columns and what happens after saving.
Where the heart shows
| Place | How it looks |
|---|---|
| Shop, category and tag pages | A round icon on the image (top right or top left), or a text link under the card's button |
| Moduly Product Grid, Product Carousel and Woo Products | The same icon, at the top of the card's own hover icons |
| Product page | A button after (or before) Add to cart, or below the summary |
| Quick view windows and other add-to-cart forms | A button after Add to cart |
| Out of stock and external products | A button after the product meta |
Settings
Buttons
| Setting | What you can set |
|---|---|
| On product cards | On / off · shop, categories, Moduly product grids and carousels |
| On the card | Icon on the image, top right · Icon on the image, top left · Text link below the card's button |
| On the product page | On / off |
| Place on the product page | After the Add to cart button · Before the Add to cart button · Below the summary |
| Icon | Heart, Star or Bookmark |
| Button text / once saved | Defaults: "Add to wishlist" and "Saved to wishlist" |
| Saved colour | The filled icon's colour |
Wishlist page
| Setting | What you can set |
|---|---|
| Page title | The title of the page created for the list (and of the My Account tab) |
| Wishlist tab in My Account | On / off |
| Take a product off the list once it is added to the cart from there | On / off |
| Private share links | Logged-in shoppers create an unguessable link, copy it or send it by email, WhatsApp, X or Facebook, make a new one or stop sharing. Anyone with the link sees the list read-only. Guests are asked to log in to share |
| Columns | Price, Stock, Date added, Add to cart button |
| Empty list text | Shown when nothing is saved |
Behaviour
| Setting | What you can set |
|---|---|
| Guests can save products | On: kept in a cookie and moved into their account at login. Off: guests are sent to log in |
| Keep a guest's list for (days) | 1–365 |
| After saving | A short message with a link to the list · just fill the icon · go to the wishlist page |
| Message position | Any corner |
Shortcodes and code
The wishlist table (put it on any page):
[mdly_wishlist]
A heart with the count, for a header:
[mdly_wishlist_count]
[mdly_wishlist_count text="Wishlist"]
A button for one product (look is button, icon or link; without id, the current product):
[mdly_wishlist_button id="42"]
In a theme template:
<?php if ( mdly_wishlist_has( get_the_ID() ) ) : ?> … <?php endif; ?>
The script fires mdly:wishlist on document after each change, with { id, saved, ids } in event.detail (API calls add op and the call's answer).
Developer API (since 1.1)
The Divi Wishlist module is built on this API, and themes and plugins can use it too.
PHP — Moduly\Extensions\Wishlist\Api
| Method | Does |
|---|---|
Api::items( [ 'search' => '', 'sort' => 'newest', 'stock' => 'all' ] ) | The visitor's list with product data: id, variation_id, attributes, variation (label / value pairs), variation_text, qty, max_qty, added, name, sku, url, image, price_html, price, in_stock, stock_text, can_add, needs_options, button_text. Sorts: newest, oldest, price_asc, price_desc, name. Stock: all, in, out. 'user' => 42 reads another account's list (a share link's owner) |
Api::add( $id, [ 'variation' => 51, 'attrs' => [], 'qty' => 2 ] ) | Save a product, or change a saved one. A variation's own ID is saved under its parent |
Api::update( $id, [ 'qty' => 3 ] ) | Change the quantity and/or variation of a saved product |
Api::remove( $id_or_ids ) | Remove one or several; returns the IDs removed |
Api::to_cart( $ids, [ 'remove' => true, 'qty' => [ $id => 2 ] ] ) | Add to the cart with each item's variation and quantity. Returns added, failed (with a reason and the product link) and removed. Out-of-stock items and variable products without a complete variation are reported, never added. remove defaults to the "take off once added" setting |
Api::share_state() / Api::share( 'enable' | 'regenerate' | 'revoke' ) | The visitor's private link: enabled, can (logged in), active, url |
Api::shared_owner( $token ) | The account a share token belongs to, or 0 |
Helpers: mdly_wishlist_has( $id ), mdly_wishlist_items( $args ), mdly_wishlist_share_url().
JavaScript — window.mdlyWishlist.api
Every method returns a promise of the server's answer, which always carries ids and count. Saved hearts and counters on the page update by themselves, and mini carts refresh after an add to cart.
const wl = window.mdlyWishlist.api;
wl.list( { search: 'tee', sort: 'price_asc', stock: 'in' } ); // → { items, share }
wl.add( 42, { variation: 51, qty: 2 } );
wl.update( 42, { qty: 3 } );
wl.remove( [ 42, 43 ] );
wl.toCart( [ 42, 43 ], { qty: { 42: 2 }, remove: true } ); // → { added, failed, removed }
wl.share( 'enable' ); // 'enable' | 'regenerate' | 'revoke' (logged in)
wl.ids(); wl.has( 42 );
AJAX — ?wc-ajax=mdly_wishlist_api
POST op = list · add · update · remove · cart · share, with id or ids[], variation, attrs[…], qty (or qty[id] for cart), remove, search, sort, stock. token reads a shared list (and cart adds from it). share needs action_name and the nonce from window.mdlyWishlist.nonce. Like WooCommerce's cart, the list belongs to the browser's session, so the other operations need no nonce.
For store owners
Products → All Products gets a sortable heart column: how many shoppers' lists hold each product.
How it stores lists
- Logged-in shoppers: user meta
_mdly_wishlist(product IDs and dates) and_mdly_wishlist_items(chosen variation and quantity). A share link's token is in_mdly_wishlist_share. - Guests: a first-party cookie,
mdly_wishlist, holding product IDs, dates, the chosen variation and the quantity only, andmdly_wishlist_who, which says whether the list is a guest's. They are removed when the shopper logs out. A guest's own pick for an "any" attribute is not kept in the cookie; choose a complete variation, or log in. - When a guest logs in, their list is merged into the account's (the guest's variation and quantity win for products on both).
- The cookie mirrors the list so the page's script can mark saved products without asking the server. That is why hearts are right even on pages served from a cache.