Skip to Content

Recipes

Practical patterns for common funnel-building tasks. Each recipe combines variables, Liquid templates, conditions, and component configuration to solve a real use case.


Progress Bar from Page Variables

Display a visual progress bar that fills as the visitor advances through the funnel.

Method 1: Custom CSS on a Stack

Create a Stack element for the progress fill. In its Custom CSS:

width: {{ page.progressPercentage }}%; height: 4px; background: linear-gradient(90deg, #6366f1, #8b5cf6); border-radius: 2px; transition: width 0.4s ease;

Wrap it in a parent Stack with a gray background to create the track:

width: 100%; height: 4px; background-color: #e5e7eb; border-radius: 2px; overflow: hidden;

page.progressPercentage updates automatically on every page change, calculating progress based on the expected path length.

Method 2: Text-based step counter

Use a Text element with Liquid:

Step {{ page.current }} of {{ page.total }}

Result: Step 3 of 8

Method 3: Percentage display

{{ page.progressPercentage }}% complete

Countdown Timer

Display a countdown that ticks down in real time using page.timeOnCurrent.

Basic countdown (seconds)

In a Text element, count down from 5 minutes (300 seconds):

{% assign remaining = 300 | minus: page.timeOnCurrent %} {% assign minutes = remaining | divided_by: 60 | floor %} {% assign seconds = remaining | modulo: 60 | round %} {% if remaining > 0 %} {{ minutes }}:{% if seconds < 10 %}0{% endif %}{{ seconds }} {% else %} 0:00 {% endif %}

page.timeOnCurrent updates every second, so the countdown ticks in real time.

Urgency message with time pressure

{% assign remaining = 600 | minus: page.timeOnCurrent %} {% if remaining > 300 %} Take your time to decide. {% elsif remaining > 60 %} This offer expires in {{ remaining | divided_by: 60 | floor }} minutes. {% elsif remaining > 0 %} Hurry! Only {{ remaining | round }} seconds left. {% else %} This offer has expired. {% endif %}

CSS-based animation timer

Animate a progress bar that shrinks over 60 seconds. In Custom CSS:

{% assign elapsed = page.timeOnCurrent %} {% assign total = 60 %} {% assign remaining = total | minus: elapsed %} {% assign percent = remaining | divided_by: total | times: 100 %} width: {% if percent > 0 %}{{ percent }}%{% else %}0%{% endif %}; height: 3px; background-color: {% if percent > 50 %}#22c55e{% elsif percent > 20 %}#eab308{% else %}#ef4444{% endif %}; transition: width 1s linear, background-color 0.3s ease;

The bar shrinks from 100% to 0% over 60 seconds, changing from green to yellow to red.


Personalized Content with User Answers

Use quiz answers to customize messaging throughout the funnel.

Personalized headline

{{ answers.name | default: "Friend" }}, your personalized {{ answers.goal | downcase }} plan is ready.

Dynamic content blocks based on experience

Use dynamic properties on the hidden property to show different content based on answers:

Beginner block — visible when:

  • Condition: answers.experience equals "beginner"

Advanced block — visible when:

  • Condition: answers.experience equals "advanced"

Each block contains different copy, images, and calls to action tailored to the audience.

Answer summary page

Here's your profile: Name: {{ answers.name }} Goal: {{ answers.goal | capitalize }} Experience: {{ answers.experience | capitalize }} Interests: {{ answers.interests | size }} selected Budget: ${{ answers.budget | default: 0 | to_fixed: 2 }}/month

Conditional social proof

{% if answers.experience == 'beginner' %} Join 50,000+ beginners who started their journey with us. {% elsif answers.experience == 'intermediate' %} Level up like 25,000+ intermediate users. {% else %} Trusted by 10,000+ expert-level professionals. {% endif %}

Conditional Visibility with Dynamic Properties

Control which components appear based on variable state.

Show discount badge after timer

  1. Add a Set Variable click action (or use a timer mechanism) to set data.showDiscount = true after a delay.
  2. On the discount badge component, set a dynamic property on hidden:
OrderConditionValue
1data.showDiscount equals truefalse (visible)
2(no condition)true (hidden)

Show different CTAs for mobile vs desktop

On the “Download App” button, set a dynamic property on hidden:

OrderConditionValue
1device.isMobile equals truefalse (visible)
2(no condition)true (hidden)

On the “Continue on Desktop” text, use the inverse:

OrderConditionValue
1device.isMobile equals truetrue (hidden)
2(no condition)false (visible)

Show payment error message

On an error text component, set visibility based on payment.error:

OrderConditionValue
1payment.error isNotEmptyfalse (visible)
2(no condition)true (hidden)

In the text content:

{{ payment.error }}

Post-purchase content

Show a “thank you” section only after a successful purchase:

OrderConditionValue
1purchase.success equals truefalse (visible)
2(no condition)true (hidden)

Product Pricing Display

Build pricing cards and comparison tables using product variables.

Simple pricing card

{{ products.monthly.displayName }} {{ products.monthly.price }}/{{ products.monthly.period }}

Result: Monthly Plan — $9.99/month

Pricing with trial

{% if products.selected.hasTrial %} Start your {{ products.selected.trialDays }}-day free trial Then {{ products.selected.price }}/{{ products.selected.period }} {% else %} {{ products.selected.price }}/{{ products.selected.period }} {% endif %}

Price comparison: monthly vs yearly

For the yearly pricing card:

{{ products.yearly.price }}/{{ products.yearly.period }} {% assign monthlyEquivalent = products.yearly.rawPrice | divided_by: 12 | to_fixed: 2 %} Just {{ products.yearly.currencySymbol }}{{ monthlyEquivalent }}/month

For the savings badge:

{% assign monthlyTotal = products.monthly.rawPrice | times: 12 %} {% assign saved = monthlyTotal | minus: products.yearly.rawPrice %} {% assign percent = saved | divided_by: monthlyTotal | times: 100 | round: 0 %} SAVE {{ percent }}%

Per-day pricing

{{ products.yearly.currencySymbol }}{{ products.yearly.rawPrice | divided_by: 365 | to_fixed: 2 }}/day

Result: $0.16/day (for a $59.99/year plan)

Use Active Style on a pricing card Stack. Set the active condition:

  • products.selectedProductId equals the product’s ID

The Active Style applies a highlight border and “Most Popular” badge styling when that product is selected.


Time-Based Formatting

Use time variables for dynamic, time-aware content.

Time spent on page

{% if page.timeOnCurrent > 60 %} {% assign mins = page.timeOnCurrent | divided_by: 60 | floor %} You've been here for {{ mins }} minute{% if mins > 1 %}s{% endif %}. {% else %} You've been here for {{ page.timeOnCurrent }} seconds. {% endif %}

Session duration

{% assign sessionSeconds = system.now | minus: session.startedAt | divided_by: 1000 %} {% assign sessionMinutes = sessionSeconds | divided_by: 60 | floor %} Session time: {{ sessionMinutes }} min

Urgency based on time on page

In the Custom CSS of a CTA button, pulse the background when the visitor has been on the page for more than 30 seconds:

{% if page.timeOnCurrent > 30 %} animation: pulse 2s infinite; {% endif %} @keyframes pulse { 0%, 100% { transform: scale(1); } 50% { transform: scale(1.03); } }

Spinner Wheel Discount

Use the Spinner Wheel element to gamify discounts.

  1. Configure the Spinner Wheel with segments (e.g., “10% Off”, “20% Off”, “Free Shipping”).
  2. After the wheel spins, the result is stored in element.{spinnerId}.result.
  3. Display the result:
{% if element.spinner1.result != '' %} Congratulations! You won: {{ element.spinner1.result }} {% endif %}
  1. Use a condition to conditionally show the claim button:
    • element.{spinnerId}.result isNotEmpty

Dialog/Drawer Controlled by Variables

Open a dialog from a button click

  1. Add a Dialog component to the page.

  2. On a button, add a Set Variable click action:

    • Variable: element.{dialogId}.open
    • Value: true
  3. Inside the dialog, add a close button with:

    • Variable: element.{dialogId}.open
    • Value: false

Conditional dialog on page load

Use a dynamic property on element.{dialogId}.open:

OrderConditionValue
1page.timeOnCurrent greaterThan 10true
2(no condition)false

This opens the dialog automatically after 10 seconds on the page.

Element variables use the component’s internal ID, which looks like element.abc123.open. You can find the exact variable name in the variable picker when configuring click actions.


Multi-Select Answer Count

Show how many items the visitor selected from a Multi Select question:

You selected {{ answers.interests | size }} interests. {% if answers.interests | size > 3 %} Great choices! You'll get recommendations for all {{ answers.interests | size }} categories. {% elsif answers.interests | size > 0 %} Select at least 3 interests for better recommendations. {% else %} Please select at least one interest to continue. {% endif %}

Use the array size in a condition to require minimum selections before allowing navigation:

  • Condition on the “Next” button’s hidden property:
    • answers.interests lessThan 1 (compares array length)
    • Value: true (hidden when less than 1 selection)
Last updated on