Skip to content

Subscriptions

Subscriptions use two selectors. <change-purchase-option> is the one-time-vs-subscribe toggle (you author two <purchase-option value="single|recurring"> chil…

Subscriptions use two selectors. <change-purchase-option> is the one-time-vs-subscribe toggle (you author two <purchase-option value="single|recurring"> children); <change-selling-plan> is the frequency selector, shown while subscribing. The one-time option is no longer a row inside change-selling-plan - it moved to the toggle. Initial state is subscribe-first: when a product authors the widget, the page opens on recurring with the variant’s first plan (Subscribe active, frequency selector filled) - default-option="single" on <change-purchase-option> opts back to one-time-first.

There are TWO pairs - one per surface. The tags above are the PDP pair (they consume productContext). Inside a <cart-line> use the cart pair instead: <change-line-purchase-option> and <change-line-selling-plan>, which read the cart line and mutate it directly. Same authored children (purchase-option / a selling-plan-option template), different host tag. Mixing them up is the #1 mistake here - the PDP tags in a cart line trip SSK-102, the cart tags on a PDP trip SSK-105.

My product page opens on “Subscribe” (recurring) instead of one-time - did something break?

Section titled “My product page opens on “Subscribe” (recurring) instead of one-time - did something break?”

No - this is the current default (changed pre-1.0). When a product subtree authors <change-purchase-option>, the initial state now preselects recurring with the variant’s first selling plan, so the widget paints “Subscribe” active and <change-selling-plan> open/filled on first load (both the live client and SSR). To open one-time-first instead, set default-option="single" on <change-purchase-option>. This is widget-scoped on purpose: a product without the widget - collection/list cards, search results, bundle members - always starts one-time (selectedSellingPlanId: null), so a card’s add-to-cart can never add a subscription silently. If you want subscribe-first but a card starts one-time, that’s expected - the card doesn’t author the widget.

My subscribe / one-time toggle shows nothing

Section titled “My subscribe / one-time toggle shows nothing”

<change-purchase-option> sets [ss-empty] on itself when the selected variant has no selling plans - that’s by design; hide it with change-purchase-option[ss-empty] { display: none }. It is not a template-clone container: author exactly two <purchase-option> children, one value="single" and one value="recurring", directly inside it (each must sit inside the toggle - SSK-108). The engine reflects [ss-active] on the current mode and aria-pressed on that option’s control; don’t author either yourself.

My <change-selling-plan> (frequency) selector shows nothing

Section titled “My <change-selling-plan> (frequency) selector shows nothing”

Three by-design cases before you suspect a bug. First, it sets [ss-empty] when the variant has no selling plans. Second, it also sets [ss-empty] whenever you’re in one-time mode (no plan selected): the frequency picker is shown only while subscribing. By default the widget is subscribe-first, so it opens filled on first paint (recurring + the first plan) - it only goes empty once the shopper switches to “one-time” in <change-purchase-option>, or from the start if you set default-option="single". Hide the empty state with change-selling-plan[ss-empty] { display: none }. Third, it is a template-clone container: it clones its first <selling-plan-option> child once per real selling plan, so that template must exist (SSK-107). Provide exactly one <selling-plan-option> (usually holding <show-selling-plan-name> / <show-selling-plan-price>); do not author one option per plan - the engine clones them. (There is no one-time-label attribute anymore - authoring it trips SSK-201; the one-time choice lives in change-purchase-option.)

The plan rows appear but the plan name / price cells are blank

Section titled “The plan rows appear but the plan name / price cells are blank”

show-selling-plan-name and show-selling-plan-price are engine-filled text holders that must sit inside a <selling-plan-option> within a <change-selling-plan> - or, in the cart, within a <change-line-selling-plan> (SSK-108). Outside either container they have no plan to read and stay empty. Keep them nested in the option template. (To override the Shopify plan names, use labels="Every week, Every 2 weeks, …" - comma-separated, in plan order; both selectors take it.)

I pick a subscription plan but the cart still adds a one-time purchase

Section titled “I pick a subscription plan but the cart still adds a one-time purchase”

Both selectors dispatch sf:selling-plan-change, which <storesynk-product> catches to update selectedSellingPlanId; add-to-cart / buy-now read that when writing the cart. If the plan isn’t honored, a selector is likely outside its <storesynk-product> (SSK-102) - so the product never learns the selection - or a value/[ss-active] was authored by hand (the engine sets those). Keep both selectors inside the product; on selling-plan-option the engine sets value to a real plan id, and clicking purchase-option value="single" is what clears the plan back to one-time.

I put <change-purchase-option> / <change-selling-plan> in my cart drawer and nothing renders

Section titled “I put <change-purchase-option> / <change-selling-plan> in my cart drawer and nothing renders”

Wrong pair for the surface. Those are the PDP selectors: they consume productContext, and inside a <cart-line> there is no <storesynk-product>, so they render nothing and the validator reports SSK-102 (its fix line names the replacement). Use the cart pair instead - <change-line-purchase-option> and <change-line-selling-plan> - inside the <cart-line> template. The authored children are identical (two <purchase-option value="single|recurring"> for the toggle; one <selling-plan-option> template holding <show-selling-plan-name> / <show-selling-plan-price> for the frequency picker), so it is a straight tag swap. Blueprint: build-patterns §2.6.

My in-cart subscribe toggle (<change-line-purchase-option>) shows nothing

Section titled “My in-cart subscribe toggle (<change-line-purchase-option>) shows nothing”

Two by-design cases first. It sets [ss-empty] on itself when that line’s product offers no selling plans - expected on a mixed cart; hide it with change-line-purchase-option[ss-empty] { display: none } (that’s why it’s safe to leave in every line template). And it must sit inside the <cart-line> template (SSK-105) - outside a line it has no cartLineContext, no plans to read, and nothing to mutate. It is not a template-clone container: author exactly two <purchase-option> children, one value="single" and one value="recurring" (SSK-108). The engine sets [ss-active] + aria-pressed on the current mode - don’t author those. There is no default-option here: the line’s own applied plan is the initial state (a line added as one-time opens on “one-time”). (Accessibility is identical to the PDP toggle: each option becomes a real keyboard control - your nested <button> if present, else the pill upgraded to role="button" + tabindex.)

My in-cart frequency picker (<change-line-selling-plan>) shows nothing

Section titled “My in-cart frequency picker (<change-line-selling-plan>) shows nothing”

Three cases. It sets [ss-empty] when the line’s product has no plans, and also whenever that line is one-time - the frequency picker only appears once the line is subscribed, so flip the toggle to “Subscribe” first. Hide the empty state with change-line-selling-plan[ss-empty] { display: none }. Third, it is a template-clone container: it clones its first <selling-plan-option> child once per plan on the line’s merchandise, so that template must exist (SSK-107). Provide exactly one - do not author one row per plan.

Switching a cart line to “one-time” doesn’t stick / the subscription stays

Section titled “Switching a cart line to “one-time” doesn’t stick / the subscription stays”

The control writes through the standard runtime: window.Shopify.actions.updateCart({ lines: [{ id, quantity, sellingPlanId }] }), sending an explicit null to strip the plan (omitting the field would leave the line’s plan unchanged). If nothing happens at all, check that the store booted (no runtime ⇒ no-op - see Add to cart does nothing) and that the control is inside the <cart-line> (SSK-105). If clicks are being ignored, the line is mid-mutation: every line action shares one in-flight guard - while ss-loading is on the <cart-line>, further clicks on that line are dropped and its controls are aria-disabled until the change settles. Style cart-line[ss-loading] (e.g. dim it) so that pause is visible.


Imported from the Storesynk skill v0.1.0 (references/known-issues.md). To change this page, change it there.