Storefront
Read the wishlist, toggle items, share lists, and react to state — the surface shoppers interact with, in any mode.
Everything on this page works in all three modes; examples use a public-mode client unless noted.
import { createEnvie } from "@getenvie/client";
const envie = createEnvie({
mode: "public",
shop: "your-store.myshopify.com",
publicKey: "env_pk_your_store_xxxxxxxx",
});Read the wishlist
const list = await envie.wishlist.get();
list.count; // 3
list.items; // [{ id, productId, variantId, handle, addedAt, … }]
list.id; // "lst_…" — or null if the shopper never saved anythingA shopper with nothing saved gets an honest empty list with id: null — reading never creates
anything. Render product cards from your own catalog data, joined on productId or handle; the
API stores identity, not presentation. (envie.getWishlist() is the same call under its
top-level name.)
Toggle (the save button)
const result = await envie.wishlist.toggle({
productId: "8123456789012",
variantId: "44012345678901", // omit for a product-level save
handle: "soft-hoodie", // always required
});
result.added; // true = now saved, false = now removed
result.count; // new total, for your badgeA product-level save and a variant save are distinct items by design — a "notify me for this variant" and a generic "I want this" are different intents.
Explicit add / remove
When you need non-toggle semantics (bulk actions, "move to wishlist" from the cart):
const item = await envie.wishlist.add({ productId: "8123456789012", handle: "soft-hoodie" });
await envie.wishlist.remove({ productId: "8123456789012" }); // by product
await envie.wishlist.remove({ itemId: item.id }); // or by item idremove is idempotent — removing something that isn't there succeeds quietly.
React to state
One subscription, shared across your whole UI (and with @getenvie/components, if you use both):
every button, badge and drawer stays in sync off a single fetch.
const unsubscribe = envie.subscribe((state) => {
state.ready; // first load complete
state.count; // for the header badge
state.items; // current items
state.config; // shop config, see below
});The listener fires immediately with the current snapshot, then on every change.
Shop configuration
const config = await envie.getConfig();
config.guestEnabled; // can anonymous shoppers save?
config.shareEnabled; // is sharing on?
config.itemLimit; // free-plan cap, or null
config.strings; // merchant's text overridesRead it before rendering optional UI — don't show a share button on a shop that disabled sharing.
Share
const { url, shareToken } = await envie.wishlist.share();
// → shareable URL on the shop's own domain
const shared = await envie.wishlist.shared(shareToken);
// → read-only list for rendering someone else's shared wishlistOnly customer-owned lists can be shared (a guest list is bound to one browser). A shared list carries items only — no owner, no personal data.
Common mistakes
- Dropping
handle. Every save needs it — it's how storefronts fetch presentation data later. Missing handle = validation error, the toggle reverts. - Polling instead of subscribing.
subscribe()is push, deduped, and free;wishlist.get()in a loop spends your rate budget on nothing. - Treating variant and product saves as the same row.
toggle({ productId })andtoggle({ productId, variantId })are independent — checkstate.itemsfor what you actually saved.