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

@shopcircle/app-manager-web-components

v1.3.2

Published

A framework-independent UI library for pricing, built with Web Components.

Readme

AppManager Web Components SDK

Lightweight, framework-agnostic pricing UI SDK with no external UI dependencies.

Build

npm install
npm run build

Demo

  • Run npm run dev and open /public/serve.html.
  • Edit source files for instant live reload.

Usage

Include the SDK via CDN in your HTML:

<script src="https://cdn.jsdelivr.net/npm/@shop-circle/app-manager-web-components@{version}/dist/app-manager-web-components.umd.js"></script>

Components

  • <app-manager-billing-page>: Use this for custom UI (no external dependencies required).
  • <app-manager-billing-page-polaris>: Use this if your app uses Shopify Polaris web components.
    • Important: You must also include the Polaris script in your HTML if it is not already present:
      <script src="https://cdn.shopify.com/shopifycloud/polaris.js"></script>

Listening for Plan Selection Events

Both <app-manager-billing-page> and <app-manager-billing-page-polaris> emit a custom event app-manager:plan-select when a user selects either the "Free plan" or chooses to "Choose later". You can listen for this event and handle each case separately:

document.addEventListener('app-manager:plan-select', e => {
  if (e.detail && e.detail.free_plan) {
    console.log('Free plan selected:', e.detail);
    // Handle free plan selection
  } else if (e.detail && e.detail.choose_later) {
    console.log('Choose later selected:', e.detail);
    // Handle choose later action
  }
});

For a free plan the detail carries:

| Key | Value | |---|---| | free_plan | true | | plan_id | Id of the plan the merchant activated | | interval | EVERY_30_DAYS or ANNUAL | | plan | The full plan object |

An app can define a free plan once per interval. Those records share a name and a price, so read interval - not the name - when you need to tell them apart.

Listening for Plan Clicks

app-manager:plan-click fires as soon as a merchant commits to a plan - before the charge is created - on every path, including a downgrade the merchant confirmed in the plan-change modal. It bubbles out of the shadow DOM, so listen on document:

document.addEventListener('app-manager:plan-click', e => {
  const { plan, plan_id, interval } = e.detail;
  console.log(`Clicked ${plan.name} (${plan_id}) on ${interval}`);
});

| Key | Value | |---|---| | plan | The full plan object, with pricing and discounts already applied | | plan_id | Id of the clicked plan | | interval | EVERY_30_DAYS or ANNUAL |

This fires for paid and free plans alike. For the outcome of a free plan selection, listen for app-manager:plan-select above - a paid plan redirects to Shopify instead.

Attributes

Both <app-manager-billing-page> and <app-manager-billing-page-polaris> share the same attributes.

Required

| Attribute | Description | |---|---| | base-url | Base URL of your AppManager API | | shop-domain | Myshopify domain of the store | | host | Shopify host parameter |

Optional

| Attribute | Values | Default | Description | |---|---|---|---| | discount-code | string | — | Promotional discount code to apply | | translations | JSON string | — | Key/value map of translated strings (see Translations section) | | show-only-highlights | "true" / "false" | "false" | Show only highlighted features in the plan cards | | default-interval | "monthly" / "yearly" | "yearly" | Which billing interval tab to open by default. Overridden by the store's active plan interval if one exists. | | large-card | "true" / "false" | "false" | Show one fewer plan per view at wide screens. Use when your plan cards are content-heavy and need more horizontal space. At viewports ≥ 1440px the slider shows 1 fewer column than usual (5→4, 4→3). Has no effect below 1440px. | | show-redirect-loader | "true" / "false" | "false" | Cover the page with a "Redirecting to Shopify…" state while the merchant is sent to Shopify to approve a charge. Opt in if the page visibly reflows during that redirect. Never shown for a free plan, which creates no charge. | | show-free-on-yearly | "true" / "false" | "false" | Mirror a monthly Free plan onto the Yearly tab for apps that have no yearly Free plan of their own. Has no effect once a yearly Free plan exists — see Free plans and billing intervals. |

Boolean attributes (show-only-highlights, large-card, show-free-on-yearly): follow HTML boolean attribute conventions — presence activates them. Only ="false" explicitly disables. Everything else (bare attribute, ="", ="true", any value) is treated as true. Not passing the attribute defaults to false.

Free plans and billing intervals

A Free plan is shown on the tab matching its own interval, exactly like a paid plan. You can configure this two ways in AppManager:

| Your plan setup | Monthly tab | Yearly tab | |---|---|---| | One Free plan (monthly) | Free | — , or Free with show-free-on-yearly | | One Free plan per interval | the monthly Free plan | the yearly Free plan |

Define a Free plan per interval when you want the merchant to land on a yearly Free record. show-free-on-yearly is only the fallback for the first setup — once a yearly Free plan exists, the yearly tab uses it and the attribute is ignored.

Plans per view

The slider picks how many plan cards to show side by side from the viewport width. Anything that does not fit stays reachable through the carousel arrows and dots.

| Viewport | Plans per view | With large-card | |---|---|---| | ≥ 1600px | 5 | 4 | | 1440 – 1599px | 4 | 3 | | 1025 – 1439px | 3 | 3 | | 641 – 1024px | 2 | 2 | | ≤ 640px | 1 | 1 |

Never more than the number of plans you actually offer. Set large-card when your cards are content-heavy and the default feels cramped.

default-interval tab selection priority

The opening tab is determined in this order:

  1. No annual plans in API → always opens Monthly (no yearly tab shown)
  2. Store has an active subscription → opens on that plan's interval (monthly or yearly)
  3. default-interval attribute → uses the value you set ("monthly" or "yearly")
  4. Nothing matched → falls back to "yearly" (the attribute's own default)

Example with all optional attributes

<app-manager-billing-page
  base-url="https://your-api.example.com"
  shop-domain="your-store.myshopify.com"
  host="your-host"
  default-interval="monthly"
  large-card
  show-free-on-yearly
  show-only-highlights="true"
  discount-code="SAVE20"
></app-manager-billing-page>

Discounts and promotions

Plan pricing on the cards reflects discounts automatically — no attribute needed beyond discount-code for a promotional code.

  • Plan discounts configured on a plan in AppManager show a discount badge, the discounted price, and the original price struck through.
  • Promotional codes passed via discount-code apply on top and take priority over a plan's own discount.
  • Time-limited discounts on the merchant's current plan show a badge with a tooltip giving the full price and the date it takes effect, so the merchant can see what they will pay once the discount ends.

A discount badge replaces the plan badge (Most popular or your own plan_badge) on that card.

Onboarding: letting merchants decide later

When a plan has Choose later enabled in AppManager and a default plan is configured, the billing page shows an "I will choose the plan later" link below the plans. Selecting it emits app-manager:plan-select with choose_later: true so your app can move the merchant on without a plan selection.

Both conditions are required — a default plan and Choose later — otherwise the link is hidden.

Translations

Both components accept a translations attribute — a JSON string mapping English keys to their translated values. Any key not provided falls back to the English default.

<app-manager-billing-page
  base-url="..."
  shop-domain="..."
  host="..."
  translations='{
    "Most popular": "Am beliebtesten",
    "Choose plan": "Plan wählen",
    "Monthly": "Monatlich",
    "Yearly": "Jährlich",
    "Frequently asked questions": "Häufig gestellte Fragen"
  }'
></app-manager-billing-page>

Both static UI labels (e.g. "Choose plan", "Monthly") and dynamic API content (e.g. plan names, feature names, FAQ questions and answers, plan details, badges) pass through the translation layer and can be overridden via this object.

Debugging translations — window.__APP_MANAGER_TRANSLATIONS__

After the component renders, a Set of every translation key encountered on the page is available on the window object:

window.__APP_MANAGER_TRANSLATIONS__
// Set(45) {
//   "Most popular",
//   "Choose plan",
//   "Basic Plan",                  ← plan name (dynamic, from API)
//   "Unlimited storage",           ← feature name (dynamic, from API)
//   "How does billing work?",      ← FAQ question (dynamic, from API)
//   ...
// }

// Convert to a plain array for easier reading:
[...window.__APP_MANAGER_TRANSLATIONS__]

Use this to verify that every key your app needs to translate is being captured. Cross-check it against the translations object you are passing — any key present in the Set but missing from your translations object will render in English.


Notes

  • This SDK does not require or load any external UI libraries by default.
  • For <app-manager-billing-page-polaris>, you must include the Polaris script as shown above if your app does not already include it.