All docsShopify
Theme integration

Shopify integration guide

Add virtual glasses try-on to your Shopify store with two Liquid snippets and one theme settings block. After setup, merchants only paste their Tryonixs API key once — the "Try On Virtually" button appears automatically on every product page.

One-time setup

Paste your pk_live_ key in Theme settings. No per-product code edits.

Smart frame images

Uses metafield, theme default, or product featured image automatically.

Self-contained SDK

Embedded loader talks to https://tryonixs.com — no external script tag.

What you get

  • tryonixs-sdk.liquid — SDK loader, initializer, modal, analytics, and automatic button injection on product pages.
  • tryonixs-button.liquid — Optional manual button for custom placement in your product template.
  • Theme settings — API key, button label, validation toggle, default frame URL, and category.

Reference files live in integrations/shopify/ in the Tryonixs repository.

Setup steps

1

Copy your Tryonixs public key

Dashboard → Integration → copy your pk_live_... key. Whitelist your Shopify store domain in Tryonixs settings if required.

2

Add theme settings

Online Store → Themes → Edit code → config/settings_schema.json. Paste the Tryonixs settings block from the docs.

3

Install Liquid snippets

Create snippets/tryonixs-sdk.liquid and snippets/tryonixs-button.liquid with the files from this repo (integrations/shopify/snippets/).

4

Include the SDK in theme.liquid

Add {% render 'tryonixs-sdk' %} once before </body>. The try-on button is injected automatically on every product page.

5

Paste your API key in theme settings

Theme editor → Theme settings → Tryonixs AR Try-On → paste API key and save. No per-product editing required.

1. Theme settings schema

In Shopify Admin go to Online Store → Themes → Edit code. Open config/settings_schema.json and append this block to the root array (add a comma after the previous entry if needed):

config/settings_schema.json
{
  "name": "Tryonixs AR Try-On",
  "settings": [
    {
      "type": "header",
      "content": "Tryonixs virtual try-on"
    },
    {
      "type": "paragraph",
      "content": "Get your public API key from the Tryonixs dashboard (Dashboard → Integration). App URL is always https://tryonixs.com."
    },
    {
      "type": "checkbox",
      "id": "tryonix_enabled",
      "label": "Enable Tryonixs try-on",
      "default": true
    },
    {
      "type": "text",
      "id": "tryonix_api_key",
      "label": "Tryonixs public API key",
      "info": "Your pk_live_... key from the Tryonixs dashboard."
    },
    {
      "type": "text",
      "id": "tryonix_button_label",
      "label": "Button label",
      "default": "Try On Virtually"
    },
    {
      "type": "checkbox",
      "id": "tryonix_skip_validation",
      "label": "Skip SDK validation (development only)",
      "default": false,
      "info": "When enabled, try-on opens without calling the validate API. Keep disabled for production so product limits and domain allowlists are enforced."
    },
    {
      "type": "text",
      "id": "tryonix_default_frame_url",
      "label": "Default frame image URL (optional)",
      "info": "Fallback transparent PNG used when a product has no custom metafield. Leave blank to use each product's featured image."
    },
    {
      "type": "text",
      "id": "tryonix_default_category",
      "label": "Default product category",
      "default": "eyeglasses"
    }
  ]
}

2. Install Liquid snippets

Create two files under snippets/ and paste the contents below.

snippets/tryonixs-sdk.liquid
{% comment %}
  Tryonixs SDK Loader, Initializer & Auto Button Injector

  Setup (merchant only needs to do this ONCE):
    1. Theme editor → Theme settings → "Tryonixs AR Try-On" → paste your API Key.
    2. Make sure this snippet is included once in theme.liquid, right before </body>:
         {% render 'tryonixs-sdk' %}

  That's it. The "Try On Virtually" button is then injected automatically on
  EVERY product page. No per-product editing required.

  Frame image passed to the SDK (same as the HTML demo's data-image-url):
    1. Product metafield  tryonixs.frame_image_url   (optional — custom transparent PNG)
    2. Theme setting      default frame URL          (optional fallback)
    3. Product featured image from Shopify           (automatic — works on every product)
{% endcomment %}

{% if settings.tryonix_enabled and settings.tryonix_api_key != blank %}
{% if product %}
  {% assign tryonix_product_image = product.metafields.tryonixs.frame_image_url.value %}
  {% if tryonix_product_image == blank %}
    {% assign tryonix_product_image = settings.tryonix_default_frame_url %}
  {% endif %}
  {% if tryonix_product_image == blank and product.featured_media %}
    {% assign tryonix_product_image = product.featured_media | image_url: width: 1200 %}
  {% elsif tryonix_product_image == blank and product.featured_image %}
    {% assign tryonix_product_image = product.featured_image | image_url: width: 1200 %}
  {% endif %}
{% endif %}

<script src="https://tryonixs.com/tryonixs-sdk.js?v=1.2.0" defer crossorigin="anonymous"></script>

<script>
  /* Tryonixs auto-init + auto button injection (runs on every product page) */
  ;(function (window, document) {
    'use strict'

    var API_KEY = {{ settings.tryonix_api_key | json }};
    var BTN_LABEL = {{ settings.tryonix_button_label | default: 'Try On Virtually' | json }};
    var SKIP_VALIDATION = {{ settings.tryonix_skip_validation | default: false | json }};

    {% if product %}
      var PRODUCT = {
        productId: {{ product.id | json }},
        productName: {{ product.title | json }},
        imageUrl: {{ tryonix_product_image | default: '' | json }},
        category: {{ product.metafields.tryonixs.category.value | default: settings.tryonix_default_category | default: 'eyeglasses' | json }}
      };
    {% else %}
      var PRODUCT = null;
    {% endif %}

    function initSdk() {
      if (!window.Tryonixs) return false;
      try {
        window.Tryonixs.init({ apiKey: API_KEY });
        if (SKIP_VALIDATION) {
          window.Tryonixs.setConfig({ skipValidation: true });
        }
      } catch (e) {
        console.error('Tryonixs:', e.message);
        return false;
      }
      return true;
    }

    function getCurrentProductImageUrl() {
      var selectors = [
        '.product__media-item.is-active img',
        '.product-media-container__item.is-active img',
        '.product__modal-opener--image img',
        '.product__main-photos img',
        '.product-single__photo img',
        '[data-product-featured-media] img',
        '.product__media img',
        '.product-media img',
        'media-gallery img'
      ];
      for (var i = 0; i < selectors.length; i++) {
        var img = document.querySelector(selectors[i]);
        if (img && (img.currentSrc || img.src)) {
          return img.currentSrc || img.src;
        }
      }
      return PRODUCT ? PRODUCT.imageUrl : '';
    }

    function openTryonFromElement(el) {
      if (!window.Tryonixs || !PRODUCT) return;

      var imageUrl = el.getAttribute('data-image-url') ||
        el.getAttribute('data-tryonixs-image') ||
        getCurrentProductImageUrl() ||
        PRODUCT.imageUrl;

      if (!imageUrl) {
        console.warn('Tryonixs: no product image found for this product.');
        return;
      }

      window.Tryonixs.open({
        apiKey: API_KEY,
        skipValidation: SKIP_VALIDATION,
        productId: el.getAttribute('data-product-id') || String(PRODUCT.productId),
        productName: el.getAttribute('data-product-name') || PRODUCT.productName,
        imageUrl: imageUrl,
        category: el.getAttribute('data-tryonixs-category') || PRODUCT.category,
        onSuccess: function (data) {
          document.dispatchEvent(new CustomEvent('tryonixs:success', { detail: data }));
        },
        onError: function (err) {
          console.error('Tryonixs error:', err.message);
        }
      });
    }

    function openTryon() {
      var btn = document.querySelector('.tryonixs-button[data-tryonixs="true"]');
      if (btn) {
        var liveImage = getCurrentProductImageUrl();
        if (liveImage) btn.setAttribute('data-image-url', liveImage);
        openTryonFromElement(btn);
        return;
      }
      openTryonFromElement({ getAttribute: function(name) {
        if (name === 'data-product-id') return String(PRODUCT.productId);
        if (name === 'data-product-name') return PRODUCT.productName;
        if (name === 'data-image-url') return getCurrentProductImageUrl() || PRODUCT.imageUrl;
        if (name === 'data-tryonixs-category') return PRODUCT.category;
        return null;
      }});
    }

    function buildButton() {
      var btn = document.createElement('button');
      btn.type = 'button';
      btn.className = 'tryonixs-tryon-btn tryonixs-button';
      btn.setAttribute('data-tryonixs', 'true');
      btn.setAttribute('data-product-id', String(PRODUCT.productId));
      btn.setAttribute('data-product-name', PRODUCT.productName);
      btn.setAttribute('data-image-url', PRODUCT.imageUrl);
      btn.textContent = BTN_LABEL;
      btn.style.cssText = [
        'display:inline-flex',
        'align-items:center',
        'justify-content:center',
        'gap:8px',
        'width:100%',
        'margin:12px 0',
        'padding:14px 24px',
        'background:#000',
        'color:#fff',
        'border:none',
        'border-radius:8px',
        'font-size:15px',
        'font-weight:600',
        'cursor:pointer',
        'letter-spacing:0.3px',
        "font-family:inherit",
        'transition:background 0.2s, transform 0.1s'
      ].join(';');
      btn.addEventListener('mouseenter', function () { btn.style.background = '#222'; });
      btn.addEventListener('mouseleave', function () { btn.style.background = '#000'; });
      btn.addEventListener('mousedown', function () { btn.style.transform = 'scale(0.98)'; });
      btn.addEventListener('mouseup', function () { btn.style.transform = 'scale(1)'; });
      btn.addEventListener('click', function (e) { e.preventDefault(); openTryon(); });
      return btn;
    }

    function findAnchor() {
      var selectors = [
        '.product-form__buttons',
        'form[action*="/cart/add"] .product-form__buttons',
        'form[action*="/cart/add"] [name="add"]',
        'form[action*="/cart/add"] button[type="submit"]',
        'product-form',
        'form[action*="/cart/add"]',
        '.product-form',
        '.product__info-container',
        '.product-single__meta',
        '.product__info'
      ];
      for (var i = 0; i < selectors.length; i++) {
        var el = document.querySelector(selectors[i]);
        if (el) return el;
      }
      return null;
    }

    function injectButton() {
      if (!PRODUCT) return;
      if (!PRODUCT.imageUrl) {
        console.warn('Tryonixs: no product image for this product. Add a featured image in Shopify admin.');
        return;
      }
      if (document.querySelector('.tryonixs-button, .tryonixs-tryon-btn')) return;

      var btn = buildButton();
      var anchor = findAnchor();

      if (anchor) {
        if (anchor.parentNode) {
          anchor.parentNode.insertBefore(btn, anchor.nextSibling);
        } else {
          anchor.appendChild(btn);
        }
      } else {
        var wrap = document.createElement('div');
        wrap.style.cssText = 'position:fixed;right:16px;bottom:16px;z-index:99998;max-width:220px;';
        btn.style.width = 'auto';
        btn.style.boxShadow = '0 6px 20px rgba(0,0,0,0.25)';
        wrap.appendChild(btn);
        document.body.appendChild(wrap);
      }
    }

    function start() {
      var ready = initSdk();
      if (!ready) {
        var tries = 0;
        var t = setInterval(function () {
          tries++;
          ready = initSdk();
          if (ready || tries > 20) {
            clearInterval(t);
            if (ready) injectButton();
            else console.error('Tryonixs: SDK failed to initialize. Check your API key in Theme settings.');
          }
        }, 100);
        return;
      }
      injectButton();
    }

    window.__tryonixOpenFromButton = function (btn) {
      if (!window.Tryonixs) { console.error('Tryonixs SDK not loaded.'); return; }
      var liveImage = getCurrentProductImageUrl();
      if (liveImage) btn.setAttribute('data-image-url', liveImage);
      openTryonFromElement(btn);
    };

    if (document.readyState === 'loading') {
      document.addEventListener('DOMContentLoaded', start);
    } else {
      start();
    }
  })(window, document);
</script>
{% endif %}
snippets/tryonixs-button.liquid
{% comment %}
  Tryonixs AR Try-On Button (OPTIONAL / MANUAL placement)

  Usage: {% render 'tryonixs-button', product: product %}

  Image URL (same order as HTML demo data-image-url):
    1. tryonixs.frame_image_url metafield (optional custom PNG)
    2. Theme default frame URL
    3. Product featured image (automatic)
{% endcomment %}

{% assign tryonix_image_url = product.metafields.tryonixs.frame_image_url.value %}
{% if tryonix_image_url == blank %}
  {% assign tryonix_image_url = settings.tryonix_default_frame_url %}
{% endif %}
{% if tryonix_image_url == blank and product.featured_media %}
  {% assign tryonix_image_url = product.featured_media | image_url: width: 1200 %}
{% elsif tryonix_image_url == blank and product.featured_image %}
  {% assign tryonix_image_url = product.featured_image | image_url: width: 1200 %}
{% endif %}

{% assign tryonix_category = product.metafields.tryonixs.category.value | default: settings.tryonix_default_category | default: 'eyeglasses' %}
{% assign tryonix_product_id = product.metafields.tryonixs.product_id.value | default: product.id %}

{% if settings.tryonix_enabled and settings.tryonix_api_key != blank and tryonix_image_url != blank %}
<div class="tryonixs-wrapper" style="margin: 12px 0;">
  <button
    type="button"
    class="tryonixs-tryon-btn tryonixs-button"
    data-product-id="{{ tryonix_product_id }}"
    data-product-name="{{ product.title | escape }}"
    data-image-url="{{ tryonix_image_url | escape }}"
    data-tryonixs-image="{{ tryonix_image_url | escape }}"
    data-tryonixs-product-id="{{ tryonix_product_id }}"
    data-tryonixs-product-name="{{ product.title | escape }}"
    data-tryonixs-category="{{ tryonix_category | escape }}"
    onclick="window.__tryonixOpenFromButton(this)"
    style="
      display: inline-flex;
      align-items: center;
      gap: 8px;
      padding: 12px 24px;
      background: #000;
      color: #fff;
      border: none;
      border-radius: 8px;
      font-size: 15px;
      font-weight: 600;
      cursor: pointer;
      letter-spacing: 0.3px;
      transition: background 0.2s, transform 0.1s;
    "
    onmouseenter="this.style.background='#222'"
    onmouseleave="this.style.background='#000'"
    onmousedown="this.style.transform='scale(0.97)'"
    onmouseup="this.style.transform='scale(1)'"
  >
    {{ settings.tryonix_button_label | default: 'Try On Virtually' }}
  </button>
</div>
{% endif %}

3. Include in theme.liquid

Open layout/theme.liquid and add this line once, right before </body>:

layout/theme.liquid
{% comment %} Add once in layout/theme.liquid, before </body> {% endcomment %}
{% render 'tryonixs-sdk' %}

4. Paste your API key

Open the theme editor → Theme settings Tryonixs AR Try-On. Enable Tryonixs, paste your pk_live_... public key from the Tryonixs dashboard, and save. Visit any product page — the try-on button should appear below Add to cart (or as a floating button if your theme layout differs).

Frame image resolution

The SDK passes an imageUrl to Tryonixs using this priority (same as the HTML data-image-url attribute):

  1. Product metafield tryonixs.frame_image_url — custom transparent PNG (best for AR)
  2. Theme setting Default frame image URL — store-wide fallback
  3. Product featured image — automatic; works on every product with a photo

Tip for best AR fit

Storefront product photos often include faces and backgrounds. For accurate overlay sizing, upload a transparent PNG of the frame only and set the frame_image_url metafield per product.

Optional product metafields

metafields.md
# Product metafields (optional — Shopify Admin → Settings → Custom data → Products)

Namespace: tryonixs

| Key              | Type        | Purpose                                      |
|------------------|-------------|----------------------------------------------|
| frame_image_url  | URL         | Custom transparent PNG for AR overlay        |
| category         | Single line | e.g. eyeglasses, sunglasses (default: theme) |
| product_id       | Single line | Override Shopify product ID for analytics    |

Frame image resolution order (same as HTML data-image-url):
1. tryonixs.frame_image_url metafield
2. Theme setting "Default frame image URL"
3. Product featured image (automatic — works on every product)

Create these in Shopify Admin → Settings → Custom data → Products. Namespace: tryonixs.

Manual button placement (optional)

By default, tryonixs-sdk.liquid injects the button automatically. To place it yourself — for example in sections/main-product.liquid — use:

sections/main-product.liquid
{% comment %} Optional: place anywhere on the product template {% endcomment %}
{% render 'tryonixs-button', product: product %}

The manual button calls window.__tryonixOpenFromButton, which is registered by the SDK snippet. It also reads the currently selected variant image from common Dawn-compatible selectors.

Theme settings reference

tryonix_enabledCheckboxMaster on/off switch
tryonix_api_keyTextYour pk_live_... public key
tryonix_button_labelTextButton label (default: Try On Virtually)
tryonix_skip_validationCheckboxSkip SDK validation (default: false — dev only)
tryonix_default_frame_urlTextOptional store-wide frame PNG URL
tryonix_default_categoryTextDefault category (default: eyeglasses)

Troubleshooting

The try-on button does not appear on product pages.

Confirm tryonix_enabled is on, tryonix_api_key is set, the product has a featured image (or metafield/default frame URL), and {% render 'tryonixs-sdk' %} is in theme.liquid before </body>.

Glasses image looks wrong or stretched.

Upload a transparent PNG with only the frame (no model face). Set tryonixs.frame_image_url on the product for a dedicated AR asset instead of the storefront photo.

Tryonixs: validation failed and try-on did not open.

Confirm your API key is active, your Shopify domain is listed in Dashboard → Settings → Allowed SDK domains, and tryonix_skip_validation is disabled in theme settings.

Camera works on desktop but not iPhone Safari.

Shopify themes must serve the store over HTTPS. Open try-on only after tapping the button — Safari blocks camera access without a direct user gesture.

I want the button in a custom position.

Disable auto-injection by removing product-page logic from tryonixs-sdk.liquid, or add {% render 'tryonixs-button', product: product %} where you want the button in your product template.

Need your API key?

Sign up for Tryonixs, open Dashboard → Integration, and copy your public key. Whitelist your *.myshopify.com or custom domain if domain restrictions are enabled.

Contact us