Skip to content

Wishlist multistore support

Many merchants serve several store views from a single Adobe Commerce instance—for example, one per country, language, or brand. Each store view can expose different products, prices, and localized experiences, so wishlist data must not leak from one store view to another.

The Wishlist drop-in supports multistore storefronts. When you pass a storeCode during initialization, the drop-in scopes browser storage and the wishlist ID cookie to the current store view.

storeCode isolates guest wishlist data and cached authenticated wishlist data per store view. It does not change the authoritative wishlist scope in Adobe Commerce.

Browser storage is scoped to the browser origin, while the wishlist ID cookie is scoped to the host. In typical storefront deployments, store views on the same domain share both unless storeCode is configured. For example, example.com/es and example.com/fr share client-side wishlist data, while es.example.com and fr.example.com do not.

When store views share client-side storage and no storeCode is set, you see the following symptoms:

  • Guest wishlist data added on one store view appears on another store view.
  • Customers see items that they did not add to the current store view.
  • Removing an item fails because the item belongs to a different store view.
  • Item counts do not match the items rendered in the wishlist.

With a storeCode, each store view keeps its own storage namespace, so these behaviors disappear.

Pass the storeCode option to the Wishlist initializer. In the Commerce Boilerplate, the value comes from the Store header defined in config.json, which holds the Adobe Commerce store view code:

scripts/initializers/wishlist.js
const headers = getHeaders('wishlist');
return initializers.mountImmediately(initialize, {
langDefinitions,
isGuestWishlistEnabled: true,
storeCode: headers.Store,
});

The Store header is defined per path in config.json, so each store view automatically resolves to its own value:

config.json
{
"public": {
"default": {
"headers": {
"all": { "Store": "default" }
}
},
"/fr/": {
"headers": {
"all": { "Store": "fr" }
}
}
}
}

The drop-in derives its storage keys and cookie name from the configured storeCode:

  • When storeCode is set to any value other than default, the drop-in appends __<storeCode> to each base key.
  • When storeCode is omitted, empty, or set to default, the drop-in uses the original unscoped keys.
DataStorageKey when storeCode is omitted or defaultKey for storeCode: 'fr'
Wishlist IDCookieDROPIN__WISHLIST__WISHLIST-IDDROPIN__WISHLIST__WISHLIST-ID__fr
Wishlist datalocalStorage (guests) / sessionStorage (authenticated)DROPIN__WISHLIST__WISHLIST__DATADROPIN__WISHLIST__WISHLIST__DATA__fr
All-items cachelocalStorage (guests) / sessionStorage (authenticated)DROPIN__WISHLIST__ALL_ITEMS__DATADROPIN__WISHLIST__ALL_ITEMS__DATA__fr

The all-items cache holds every wishlist item across all pages so that the WishlistToggle container renders the correct heart-icon state regardless of pagination. It is scoped the same way as the wishlist data.

Scoping the storage keys isolates what the drop-in persists in the browser, but it does not change which items Adobe Commerce returns. For authenticated customers, the GraphQL layer determines the authoritative scope:

  • Wishlists are shared between store views that belong to the same website. For example, the es, fr, and de store views under a European website share the same wishlist.
  • Wishlists are not shared between different websites. Wishlists created on the European website are not visible on the US website, and vice versa.

Guest wishlists are never affected by this, because they live entirely in browser storage and are isolated by the storeCode alone.

Wishlist links must include the store view path prefix so that customers stay inside their store view when navigating. The Commerce Boilerplate handles this automatically with decorateLinks, which prepends the current root path to internal links—for example, /wishlist becomes /fr/wishlist on French pages.

If you render wishlist links outside of decorated content, prefix them with the active root path yourself. For details, see Localizing links.

Use this checklist to confirm that multistore wishlist behavior works end to end:

  1. Confirm that config.json defines a distinct Store header value for each store view path.
  2. Confirm that scripts/initializers/wishlist.js passes storeCode: headers.Store.
  3. As a guest, add an item on one store view, switch to another store view, and verify that the wishlist is empty.
  4. Inspect browser storage and confirm that the keys carry the __<storeCode> suffix for non-default store views.
  5. As an authenticated customer, add an item on one store view and verify that items_count matches the rendered items on every store view of the same website.
  6. For an item available in both store views, remove it from a store view other than the one where it was added, and confirm that the operation succeeds without a “does not belong to the wishlist” error.
  7. Confirm that wishlist links keep the store view path prefix after navigation.
SymptomLikely causeResolution
Guest wishlist items appear on every store viewstoreCode not passed to the initializerPass storeCode: headers.Store in scripts/initializers/wishlist.js
Item count is 0 but items renderitems_v2.items is not store-view-scopedUpgrade to SCP 4.7.17 or later (4.7 line), or SCP 4.8.19 or later (4.8+ line)
The wishlist item with ID "…" does not belong to the wishlistRemoval attempted from a different store view scopeUpgrade to SCP 4.7.17 or later (4.7 line), or SCP 4.8.19 or later (4.8+ line)
Wishlist links drop the store view path prefixLinks rendered outside of decorated contentPrefix links with the active root path or run decorateLinks