Skip to content

Subscriptions

The Commerce extension renders subscription pricing from project subscription groups. A group holds one or more plans, each plan holds the features it includes and the billing frequencies it can be bought on, and helper tags render a whole group, a comparison table, or a single plan. Every subscription token and loop is populated only when the commerce extension is enabled.

Plan groups and loops

{each subscription_group[<id>].subscriptions as=sub} walks the plans inside one group. Replace <id> with the numeric id of the subscription group, and choose any alias for the current plan. Inside the loop, read the plan with {sub.*}; an alias such as plan works the same way, so {plan.name} and {sub.name} are the same field.

html
{each subscription_group[1].subscriptions as=sub}
  <article class="plan">
    <h3>{sub.name}</h3>
    <p>{sub.description}</p>
    <span>{sub.price}</span>
  </article>
{/each}

The loop renders nothing when the group has no plans. Wrap it in {if ...} when an empty group should show a message instead of a blank section.

Plan fields

TokenMeaning
{sub.id}Plan id.
{sub.name}Plan name.
{sub.price}Plan price.
{sub.description}Plan description.
{sub.icon}Plan icon URL, when set.
{sub.icon_avif}AVIF source for the plan icon, when available.
{sub.is_popular}True when the plan is marked popular.
{sub.has_trial}True when the plan offers a trial.
{sub.is_current}True when the signed-in customer is already on this plan.
{sub.is_locked}True when the plan cannot be selected.
{sub.has_button_link}True when the plan has a custom button link.
{sub.button_text}Label for the plan button.
{sub.button_link_url}Destination for the plan button.
{sub.metadata.<key>}Value stored under a plan metadata key.

A plan can carry design metadata such as {sub.metadata.subtitle} or {sub.metadata.key}. Read a metadata value with its exact key.

html
{each subscription_group[1].subscriptions as=sub}
  <article class="plan {if sub.is_popular}popular{/if}">
    {if sub.icon}
      <img src="{sub.icon}" alt="{sub.name}">
    {/if}
    <h3>{sub.name}</h3>
    <p>{sub.description}</p>
    {if sub.is_locked}
      <button disabled>Unavailable</button>
    {elseif sub.has_button_link}
      <a href="{sub.button_link_url}">{sub.button_text}</a>
    {/if}
  </article>
{/each}

Billing frequencies

{each sub.frequencies as=freq} is nested inside the plan loop. Each plan exposes one or more billing frequencies, and each frequency carries its own price, interval, discount, and intro period.

TokenMeaning
{freq.price}Price charged on this frequency.
{freq.original_price}Price before the discount.
{freq.monthly_equivalent}Price expressed per month, useful for longer cycles.
{freq.interval_unit}Billing unit, such as month, year, or week.
{freq.interval_count}Number of units between charges.
{freq.is_monthly}True when the frequency bills every month.
{freq.is_yearly}True when the frequency bills every year.
{freq.has_discount}True when a discount applies to the frequency.
{freq.discount_amount}Discount value.
{freq.discount_type}How the discount is expressed, such as percent or fixed.
{freq.formatted_discount}Discount ready to print.
{freq.intro_duration}Length of the intro or trial period.
{freq.intro_unit}Unit for the intro period.
html
{each sub.frequencies as=freq}
  {if freq.is_monthly}
    <div class="price">
      <span>{freq.price}</span>
      <span>per {freq.interval_unit}</span>
    </div>
    {if freq.has_discount}
      <span class="was">{freq.original_price}</span>
      <span class="save">Save {freq.formatted_discount}</span>
    {/if}
    {if freq.intro_duration}
      <span>{freq.intro_duration} {freq.intro_unit} free trial</span>
    {/if}
  {/if}
  {if freq.is_yearly}
    <div class="price">
      <span>{freq.price}</span>
      <span>per year, {freq.monthly_equivalent} per month</span>
    </div>
  {/if}
{/each}

Add limit=1 to show only the first frequency: {each sub.frequencies as=freq limit=1}.

Features

{each sub.features as=feature} is nested inside the plan loop. Each feature exposes its name.

TokenMeaning
{feature.name}Feature name.
html
<ul class="features">
  {each sub.features as=feature limit=3}
    <li>{feature.name}</li>
  {/each}
</ul>

Helper tags

Three helper tags render prepared subscription layouts. Each takes the group id, and the single plan tag also takes the plan id.

TagWhat it renders
{subscription_group id=<id>}Every plan in the group.
{subscription_comparison group_id=<id>}A feature comparison for the plans in the group.
{subscription group_id=<id> id=<id>} ... {/subscription}One plan, so its fields are available as {subscription.*}.
html
{subscription_group id=1}
html
{subscription_comparison group_id=1}

The single plan tag scopes one plan and exposes the same fields under {subscription.*}, including the nested features and frequencies loops.

html
{subscription group_id=1 id=2}
  <h1>{subscription.name}</h1>
  <p>{subscription.description}</p>
  <span>{subscription.price}</span>
  {if subscription.has_button_link}
    <a href="{subscription.button_link_url}">{subscription.button_text}</a>
  {/if}
  {each subscription.frequencies as=freq}
    <li>{freq.price} per {freq.interval_unit}</li>
  {/each}
{/subscription}

Group-level tokens

A group also exposes summary values. {subscription_group[<id>].max_discount} is the largest discount available in the group, and the upgrade and downgrade flags describe what the group allows.

TokenMeaning
{subscription_group[<id>].max_discount}Largest discount available in the group.
{subscription_group[<id>].upgrade_allowed}True when plans in the group can be upgraded.
{subscription_group[<id>].downgrade_allowed}True when plans in the group can be downgraded.
{subscription_group[<id>].allow_multiple_subscriptions}True when a customer can hold more than one plan from the group.
html
<span>Save up to {subscription_group[1].max_discount} with annual billing</span>

See also