Shoppable Content

This guide covers how to implement shopping cart management, product analytics tracking, product hydration and product wishlist to create engaging shopping experiences with the Storyly Placement SDK for Web.

What is Shoppable Content

Storyly Placement SDK provides powerful shoppable features that enable seamless e-commerce integration within widgets.

📘

Features

  • Manage shopping cart operations with real-time feedback
  • Track product analytics through comprehensive event callbacks
  • Hydrate products from your latest data source, ensuring up-to-date pricing, availability, and product information
  • Product wishlist for user's favorite products

📘

Architecture

Event-Based Control

Cart and wishlist operations are handled through events registered on the <storyly-placement> element via placement.on(eventName, callback), giving your application complete control over state management, API integration, and user experience.

Approve-Reject Mechanism

Each cart and wishlist event delivers a payload containing two callbacks — onSuccess and onError. You must explicitly call one of them to inform the SDK of the operation result, ensuring your backend always has the final say before the widget UI updates.

🚧

Prerequisites

  • Storyly Placement SDK integrated and initialized in your Web application as described in Initial Setup.
  • Shoppable Content user guide must be followed and integrations on Storyly Dashboard must be completed.
  • Wishlist feature must be turned on under Brand settings before creating shoppable content on the Storyly Dashboard.

Update Shopping Cart

This section explains how to update cart which allows users to add products to their shopping cart directly from Storyly widgets. Storyly SDK notifies your app when cart operations occur, giving you full control over the cart logic.

Setup Listener

The storyCartUpdate event is triggered when a user attempts to add, update, or remove items in the cart through the widget. Your app is responsible for handling the actual cart operation and informing Storyly SDK of the result.

The event payload contains:

FieldTypeDescription
productProductThe product being added, updated, or removed.
product_type"ProductAdded" | "ProductUpdated" | "ProductRemoved"The type of cart update.
onSuccess(cart) => voidCall to acknowledge the operation and update the widget UI (e.g., show success animation).
onError({ message }) => voidCall to reject the operation and surface an error message in the widget UI.

🚧

Warning

You must call either onSuccess or onError to inform Storyly SDK of the operation result. This allows Storyly SDK to update the UI accordingly (e.g., show success animation or error message).

storylyPlacement.on("storyCartUpdate", (data) => {
  const { product, product_type, onSuccess, onError } = data;

  // Call your cart API to check product availability
  // then update the cart based on `product_type`

  // Notify SDK in case of success
  onSuccess({
    products: "all_products_in_cart",
    price: "total_price",
    sales_price: "total_sales_price",
    price_currency: "currency",
  });

  // Notify SDK in case of fail
  onError({
    message: "Failed to add product to your cart",
  });
});

Track Product Analytics

This section explains how to track user behavior and integrate with your analytics platforms in the context of product. Product-related actions are delivered through dedicated per-event callbacks on the <storyly-placement> element and are also mirrored on the unified userEvent stream.

Product Analytic Events

This section shows the list of product analytic events and their description on the Web SDK.

EventDescription
storyCartUpdateA cart update is requested (add / update / remove). Includes onSuccess / onError so your app can approve or reject the change. product_type is ProductAdded, ProductUpdated, or ProductRemoved.
storyCartClickedThe user opened the in-story cart view (e.g., clicked "Go to Cart" from the success sheet).
storyWishlistUpdateA wishlist update is requested. type is addWishlist or removeWishlist. Includes onSuccess / onError.
storyNoCartIntegrationA cart-related interaction occurred but no cart integration is configured. Payload is the product object the user attempted to interact with.
productTagClickedA product tag inside a story was tapped.
productTagExpandedA product tag tooltip was expanded.
storylyHydrationProduct data hydration was requested by the widget — see Hydrate Products.

Setup Listener

Register a callback for each event you want to track. Every event is also available on the unified userEvent stream, discriminated by data.event (or data.type for Banner).

// Per-event callbacks
storylyPlacement.on("storyCartClicked", (data) => {
  // Track "Go to Cart" click, e.g. navigate to the cart page
});

storylyPlacement.on("storyNoCartIntegration", (product) => {
  // Track cart interactions when no cart integration exists
});

storylyPlacement.on("productTagClicked", (data) => {
  // Track product tag clicks
});

// Or use the unified userEvent stream and switch on the discriminator
storylyPlacement.on("userEvent", (data) => {
  switch (data.event ?? data.type) {
    case "storyCartClicked":
      // ...
      break;
    case "storyWishlistUpdate":
      // ...
      break;
    // ...handle every event you care about
  }
});

Hydrate Products

This section explains how to hydrate products with the latest information from your own data source. This ensures that prices, availability, and other product details are always up-to-date.

📘

Why use hydration?

  • Show latest product data instead of the daily-updated product feed data
  • Ensure real-time pricing accuracy
  • Display current stock availability
  • Show personalized product information
  • Maintain consistency between your app and Storyly widgets

Setup Listener

The storylyHydration event is triggered when Storyly widgets contain products that may need to be updated. The payload is an array of product identifiers found in the currently rendered content.

Fetch the latest product data from your backend for those identifiers, then hand the enriched product objects back to the SDK via

storylyPlacement.on("storylyHydration", async (products) => {
  console.log(products)
});

Each product object passed to hydrateProducts should follow the standard product schema (product_id, product_group_id, title, price, sales_price, price_currency, availability, image_urls, url, variant, etc.).

setStorylyHydration

Once you have fetched the latest product data for the IDs delivered by storylyHydration, call setStorylyHydration(products) on the widget instance to feed the enriched products back to the placement. The widget instance is obtained from the widgetReady event, following the same pattern as Hydrate Wishlisted Products.

📘

Note

Product hydration is not exposed on every widget type — only widgets that render products expose setStorylyHydration. Check for the method (or type-cast the widget) before invoking it so the call is only made on widgets that support it.

storylyPlacement.on("widgetReady", async (widget) => {
  // Fetch the latest product data from your backend
  const latestProducts = await fetchLatestProducts();
  // Hydrate the widget with the updated products
  widget.setStorylyHydration(latestProducts);
});

Product Wishlist

This section explains how to add products to the wishlist directly from Storyly widgets. You can pre-populate the wishlist state and handle wishlist updates through the dedicated event.

Setup Listener

The storyWishlistUpdate event is triggered when a user adds or removes a product from their wishlist through the widget. Your app handles the actual wishlist operation and informs Storyly SDK of the result.

The event payload contains:

FieldTypeDescription
productProductThe product being added to or removed from the wishlist.
type"addWishlist" | "removeWishlist"The requested wishlist operation.
onSuccess() => voidCall to acknowledge the operation and update the wishlist UI in the widget.
onError({ message }) => voidCall to reject the operation and surface an error state in the widget UI.

🚧

Warning

You must call either onSuccess or onError to update the wishlist UI state in the widget.

storylyPlacement.on("storyWishlistUpdate", async (data) => {
  const { product, type, onSuccess, onError } = data;

  try {
    switch (type) {
      case "addWishlist":
        // Add to wishlist
        await addToWishlist(product);
        break;
      case "removeWishlist":
        // Remove from wishlist
        await removeFromWishlist(product);
        break;
    }

    // Notify SDK in case of success
    onSuccess();
  } catch (error) {
    // Notify SDK in case of fail
    onError({ message: "Wishlist operation failed" });
  }
});

Hydrate Wishlisted Products

This section explains how to hydrate products that are already in the user's wishlist. This ensures the wishlist state is correctly displayed in the widget UI (e.g., filled heart icon on products the user has already saved).

Pass the list of products already in the user's wishlist to hydrateWishlist. Typically you do this once the widget is ready and any time the user's wishlist changes outside of Storyly.

storylyPlacement.on("widgetReady", async (widget) => {
  // Fetch the user's current wishlist
  const wishlistProducts = await getUserWishlist();

  // Hydrate the wishlist
  widget.setWishlistHydration(wishlistProducts);
});

Supplemental Product Feeds

Storyly allows you to integrate your multi-market product feeds depending on language or country. You can use this feature to show different products to users in different countries or with different languages.

You need to set the locale parameter in the placement config to the end-user's locale in the IETF BCP 47 format.

storylyPlacement.init({
  token: "<your_placement_token>",
  locale: "en-GB", // "es-ES" / "pt-PT" / "tr-TR" / "he-IL" / ...
});