Wix Integration

Embed the PersoScan body map in Wix and route zone clicks with Velo, including the HTML embed element, the relay listener and the page code that navigates.

Wix is the one platform where the iframe cannot navigate the page itself. Everything goes through Velo, and that changes the shape of the integration.

Prerequisites

  • You can edit page/template code in Wix.
  • You have category, tag, or collection pages ready for target redirects.
  • You can test in a published or preview storefront.

Why Wix needs two pieces of code

A Wix HTML embed element is itself an iframe, sandboxed and isolated from the page around it. The widget sits inside that, which means your zone click is two frames deep and cannot touch window.location of the real page.

So the integration splits in two. The HTML embed holds the widget and a listener that relays the selection outward with window.parent.postMessage. Then Velo page code receives it with $w('#html1').onMessage and calls wixLocation.to().

Destinations are usually a query string rather than a category path, because Wix Stores filtering runs client side: /shop?bodypart=knee, read back in Velo and applied to the product list. That is why the snippet maps zone keys to tag values instead of URLs.

1. Prepare Zone Mapping

  1. Start with grouped zone IDs (head, neck, lower-back, and so on).
  2. Map each zone key to one destination URL or slug.
  3. Define a fallback destination for unmapped zones.

2. Embed And Listen

Add this code and update mapping values for your store:

<!-- Wrap the iframe in a div to control its dimensions.
     To fill an entire section, set both the div and iframe height to 100% instead. -->
<div style="height:650px;width:100%;">
<iframe
  id="persoscan-widget"
  src="https://widget.persoscan.com?license=your-license-key"
  style="width:100%;height:650px;border:none;border-radius:8px;color-scheme:light dark;"
  allow="fullscreen"
  title="PersoScan"
></iframe>
</div>

<script>
  // Derives the origin safely, do not modify this line
  var WIDGET_ORIGIN = new URL("https://widget.persoscan.com").origin;

  // Map zone keys to your Wix tags, edit to match your store
  var zoneToTag = {
    "head":           "head-care",
    "neck":           "neck-support",
    "upper-back":     "upper-back-support",
    "lower-back":     "lower-back-support",
    "left-shoulder":  "shoulder-support",
    "right-shoulder": "shoulder-support",
    "left-arm":       "arm-support",
    "right-arm":      "arm-support",
    "left-hand":      "hand-wrist",
    "right-hand":     "hand-wrist",
    "left-leg":       "leg-knee",
    "right-leg":      "leg-knee",
    "left-foot":      "foot-care",
    "right-foot":     "foot-care"
  };

  // Guard against duplicate listeners if Wix re-renders the element
  if (!window.__persoscanWixEmbedInstalled) {
    window.__persoscanWixEmbedInstalled = true;

    window.addEventListener("message", function(event) {
      if (event.origin !== WIDGET_ORIGIN) return;

      var msg = event.data;
      if (!msg || msg.source !== "persoscan-widget" || msg.type !== "zone-selected") return;

      var data = msg.data || {};
      var zoneKey = (typeof data === "string" ? data : (data.zoneKey || data.key || data.zoneId || data.id || "")) + "";
      zoneKey = zoneKey.trim().toLowerCase().replace(/_/g, "-");

      var tag = zoneToTag[zoneKey] || "";

      // Relay zone selection to Wix page code (Velo). Navigation happens there, not here
      window.parent.postMessage({ source: "persoscan-wix", type: "navigate", tag: tag }, "*");
    });
  }
</script>

4. Add Platform Script

Use this page-level script for message handling and navigation:

import wixLocation from 'wix-location';

$w.onReady(() => {
  // Replace '#html1' with the actual ID of your Embed HTML element.
  // Select the element in the editor and check the ID in the inspector panel on the right.
  $w('#html1').onMessage((event) => {
    const { source, type, tag } = event.data || {};
    if (source !== 'persoscan-wix' || type !== 'navigate') return;

    // Navigates to /shop with a bodypart query parameter.
    // Note: Wix does not filter products by query param automatically.
    // To show filtered results, add Velo filtering code on your Shop page
    // that reads wixLocation.query.bodypart and filters the product gallery,
    // or navigate directly to a Wix collection page instead of /shop.
    wixLocation.to(tag ? '/shop?bodypart=' + encodeURIComponent(tag) : '/shop');
  });
});

3. Validate End-To-End

  1. Open the integration page.
  2. Click at least five mapped zones.
  3. Confirm each click redirects to the expected destination.
  4. Confirm unmapped zones use fallback destination.

Common Mistakes

Velo code in the wrong scope

Problem: $w('#html1').onMessage never fires.

Cause: The handler was registered outside $w.onReady, so it ran before the element existed.

Solution:

  1. Register the handler inside $w.onReady(function () { ... }).
  2. Confirm the element ID in the editor matches the selector, #html1 is a default that often changes.

Origin mismatch blocks events

Problem: Clicks produce no redirects.

Cause: Event origin check does not match actual widget origin.

Solution:

  1. Derive origin from widget URL (new URL(WIDGET_URL).origin).
  2. Compare against event.origin.
  3. Keep strict source check (persoscan-widget).

Zone mapping keys do not match runtime keys

Problem: Some body parts never redirect.

Cause: Mapping object uses wrong key format.

Solution:

  1. Use exact zoneId values from Zone Reference.
  2. Keep keys lowercase with hyphen separators.

Next Steps

Last updated: March 12, 2026