npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

joy-subscription-sdk

v2.0.1

Published

Headless SDK for AVADA Subscription App - integrate subscription widgets with React, Vue, Next.js, Hydrogen

Downloads

346

Readme

joy-subscription-sdk

Headless SDK for integrating Joy Subscription with Shopify headless stores.

Features

  • Lightweight - SDK only ~2.3KB gzipped
  • Modular - Import only what you need
  • Auto script loading - SDK handles scripttag loading with deduplication
  • Event-driven - Listen for widget events (plan selection, variant change, etc.)
  • Framework agnostic - Works with React, Vue, Next.js, Hydrogen, etc.

Installation

Option A: NPM (React, Vue, Next.js, Hydrogen)

npm install joy-subscription-sdk
import { WidgetSDK } from "joy-subscription-sdk/widget";

const sdk = new WidgetSDK({
  shopDomain: "your-store.myshopify.com",
  storefrontAccessToken: "your-storefront-access-token",
});

await sdk.initProduct("product-handle");

Option B: CDN Script Tag (Vanilla JS, Liquid, Webview)

No bundler needed. Add a script tag and configure via window.AVADA_SUBSCRIPTION_CONFIG.

<script>
  window.AVADA_SUBSCRIPTION_CONFIG = {
    shopDomain: "your-store.myshopify.com",
    storefrontAccessToken: "your-storefront-access-token",
  };
</script>
<script
  src="https://unpkg.com/joy-subscription-sdk/dist/subscription-sdk.umd.js"
  defer
></script>

Once loaded, the SDK is available at window.AvadaSubscription:

<script>
  window.addEventListener("subscription:ready", function (e) {
    var sdk = e.detail.sdk;

    // Initialize product widget
    sdk.initProduct("product-handle");

    // Listen for add-to-cart events
    sdk.on("add-to-cart", function (data) {
      console.log("Add to cart:", data.lines, data.discountCodes);
    });
  });
</script>

Option C: UMD Script Tag (Manual control)

Load individual feature bundles for smaller size:

<!-- Load only what you need -->
<script src="https://unpkg.com/joy-subscription-sdk/dist/productBundle.umd.js"></script>
<script>
  // Global: window.ProductBundleSDK
  var sdk = new ProductBundleSDK.ProductBundleSDK({
    shopDomain: "your-store.myshopify.com",
    storefrontAccessToken: "your-storefront-access-token",
  });

  sdk.on("add-to-cart", function (data) {
    console.log("Bundle add to cart:", data);
  });

  sdk.initProductBundle("product-handle");
</script>

| UMD Bundle | Script | Global Variable | | ------------------ | ------------------------- | -------------------------- | | Widget | widget.umd.js | window.WidgetSDK | | Portal | portal.umd.js | window.PortalSDK | | Box | box.umd.js | window.BoxSDK | | Product Bundle | productBundle.umd.js | window.ProductBundleSDK | | CDN (all features) | subscription-sdk.umd.js | window.AvadaSubscription |


Configuration (NPM)

Per-instance config

import { WidgetSDK } from "joy-subscription-sdk/widget";

const sdk = new WidgetSDK({
  shopDomain: "your-store.myshopify.com",
  storefrontAccessToken: "your-storefront-access-token",
});

Global config (recommended for SPA)

Configure once at app entry point, use anywhere without passing config.

// _app.js, main.js, or app entry point
import { SubscriptionSDK } from "joy-subscription-sdk";

SubscriptionSDK.configure({
  shopDomain: process.env.SHOPIFY_DOMAIN,
  storefrontAccessToken: process.env.STOREFRONT_TOKEN,
});

Then use anywhere:

import { WidgetSDK } from "joy-subscription-sdk/widget";

// Option A: New instance (uses global config)
const sdk = new WidgetSDK();

// Option B: Singleton (recommended - shared across components)
const sdk = WidgetSDK.getInstance();

| Pattern | Use Case | | ------------------------- | --------------------------------------------- | | new WidgetSDK() | Component-scoped, cleaned up with component | | WidgetSDK.getInstance() | Shared across app, persists during navigation |

Config Options

| Option | Required | Description | | ----------------------- | -------- | ------------------------------------------------------ | | shopDomain | Yes | Shopify shop domain (e.g., store.myshopify.com) | | storefrontAccessToken | Yes | Shopify Storefront API access token | | apiVersion | No | Storefront API version (default: 2025-01) | | apiBaseUrl | No | Custom API base URL (default: https://sub.joyapp.gg) |


Bundle Options

Standalone Bundles (recommended for single feature)

import { WidgetSDK } from "joy-subscription-sdk/widget"; // 2.3KB gzip
import { PortalSDK } from "joy-subscription-sdk/portal"; // 2.6KB gzip
import { BoxSDK } from "joy-subscription-sdk/box"; // 2.2KB gzip
import { ProductBundleSDK } from "joy-subscription-sdk/productBundle"; // 2.4KB gzip
import { SubscriptionSDK } from "joy-subscription-sdk"; // 3.5KB gzip (all)

Light Bundles (for multiple features)

import "joy-subscription-sdk/core"; // 2.5KB gzip
import { WidgetSDK } from "joy-subscription-sdk/widget/light"; // 1.0KB
import { PortalSDK } from "joy-subscription-sdk/portal/light"; // 1.3KB
import { BoxSDK } from "joy-subscription-sdk/box/light"; // 0.8KB
import { ProductBundleSDK } from "joy-subscription-sdk/productBundle/light"; // 1.0KB

| Scenario | Standalone | Light + Core | Recommended | | --------------- | ---------- | ------------ | ----------- | | Widget only | 2.3KB | 3.5KB | Standalone | | Portal only | 2.6KB | 3.8KB | Standalone | | Widget + Portal | 4.9KB | 4.8KB | Light | | All 3 features | 7.1KB | 5.6KB | Light |


Events

NPM usage

const unsubscribe = sdk.on("add-to-cart", (data) => {
  console.log(data.lines, data.discountCodes);
});

// Stop listening
unsubscribe();

// Cleanup all listeners
sdk.destroy();

CDN / Vanilla JS usage

// SDK ready event
window.addEventListener("subscription:ready", function (e) {
  var sdk = e.detail.sdk;
  // SDK is ready, initialize features
});

// Add-to-cart event (from widget or bundle)
window.addEventListener("avada:add-to-cart", function (e) {
  var data = e.detail;
  console.log("Lines:", data.lines);
  console.log("Discount codes:", data.discountCodes);
  console.log("Redirect to:", data.redirectTo); // 'checkoutPage' or 'cartPage'

  if (data.redirectTo === "checkoutPage") {
    // Redirect to checkout after adding to cart
  } else {
    // Redirect to cart page (default)
  }
});

// Plan selected event (from widget)
window.addEventListener("avada:plan:selected", function (e) {
  console.log("Selected plan:", e.detail);
});

Available Events

| Event | Description | | -------------------- | ------------------------------------- | | subscription:ready | SDK initialized and ready (CDN only) | | add-to-cart | User clicked add to cart (bundle/box) | | plan:selected | User selected a subscription plan | | init | Widget initialized | | portal:init | Customer portal initialized |

add-to-cart Event Data

| Field | Type | Description | | --------------- | -------- | --------------------------------------------------- | | lines | Array | Cart line items to add | | discountCodes | Array | Discount codes to apply | | redirectTo | String | Where to redirect after adding: 'checkoutPage' or 'cartPage' |

Common API (BaseSDK)

All SDKs (WidgetSDK, PortalSDK, BoxSDK, ProductBundleSDK) inherit these methods:

// Listen for events
const unsubscribe = sdk.on('plan:selected', (data) => { ... });

// Stop listening
unsubscribe();

// Get current selected plan
const plan = sdk.getSelectedPlan();

// Cleanup all listeners
sdk.destroy();

Guides

| Guide | Description | | -------------------------------------------- | --------------------------------------- | | Product Widget | Subscription widget on product pages | | Product Bundle | Product bundle widget on product pages | | Subscription Box | Subscription box on dedicated pages | | Customer Portal | Customer subscription management portal |


Advanced Usage

Direct Client Access

import { SubscriptionClient } from "joy-subscription-sdk/core";

const client = new SubscriptionClient({
  shopDomain: "store.myshopify.com",
  storefrontAccessToken: "xxxxx",
});

const shopData = await client.getShopData();
const productData = await client.getProductData("handle");
const combined = await client.getShopAndProductData("handle");

Manual Script Loading

await sdk.initProduct("handle", { autoLoadScript: false });

import { loadScript, SCRIPTS } from "joy-subscription-sdk/core";
await loadScript(SCRIPTS.WIDGET);

Script Utilities

import {
  loadScript,
  isScriptLoaded,
  preloadScript,
  getScriptUrl,
  SCRIPTS,
} from "joy-subscription-sdk/core";

if (!isScriptLoaded(SCRIPTS.WIDGET)) {
  await loadScript(SCRIPTS.WIDGET);
}

getScriptUrl(SCRIPTS.WIDGET);
// -> https://cdn-joy-sub.avada.io/scripttag/avada-subscription-main.min.js?v=1.0.0

License

MIT