Envie
ReferenceSDK

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 anything

A 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 badge

A 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 id

remove 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 overrides

Read 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 wishlist

Only 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 }) and toggle({ productId, variantId }) are independent — check state.items for what you actually saved.

On this page