Rivo Account Widget SDK

Rivo Account Widget SDK

This guide shows developers how to extend and customize the Rivo Account Widget on a Shopify storefront. It focuses on the SDK surfaces exposed by the widget runtime: global objects, Alpine stores, hash routing, and built‑in JavaScript hooks.


Quick Start

The Account Widget runs on storefront pages and bootstraps an Alpine.js instance with a custom prefix (rivo-ax-). When it loads, these globals are available:

  • window.RivoProfileAlpine – the Alpine instance
  • window.RivoAPI – the Rivo API client (preconfigured for the shop)
  • window.Rivo – global Rivo config and common data

You can use these to read and customize widget state, inject dynamic content, or respond to widget navigation.


Global Objects

window.RivoProfileAlpine

The Alpine.js instance used by the Account Widget.

  • Prefix is set to rivo-ax- to avoid conflicts with other Alpine instances.
  • Use stores to access data and settings.

Example:

<span rivo-ax-text="$store.rivo_profile_customer.email"></span>

window.RivoAPI

A preconfigured API client for customer and widget data. It’s used by the widget internally and is safe to call from custom hooks.

Common methods used by the widget:

window.RivoAPI.identify_customer("loggedin");
window.RivoAPI.points_logs("loggedin");
window.RivoAPI.orders("loggedin", 10);
window.RivoAPI.order("loggedin", orderId);
window.RivoAPI.order_payment_details("loggedin", orderId);
window.RivoAPI.shopify_customer("loggedin");
window.RivoAPI.preferences();
window.RivoAPI.favorite_products("loggedin", currencyCode);
window.RivoAPI.favorite_collections("loggedin");
window.RivoAPI.update_saved_cart_collection(productIds, "add", currencyCode);
window.RivoAPI.products(productIds, "shopify", currencyCode);
window.RivoAPI.popular_products("shopify", currencyCode);
window.RivoAPI.membership();
window.RivoAPI.gift_card_lookup(giftCardUid);
window.RivoAPI.gift_card_code_uid(code);
window.RivoAPI.referral_stats("loggedin");

window.Rivo

Rivo runtime configuration and storefront context:

  • window.Rivo.loy_config – loyalty config + widget settings
  • window.Rivo.common.customer – current customer (if logged in)
  • window.Rivo.common.product – current product (if any)
  • window.Rivo.common.collection – current collection (if any)
  • window.Rivo.global_config – global app settings

Alpine Stores (Key Data)

The widget stores its state in Alpine stores. These are the most useful for customization:

StoreDescription
rivo_profile_customerCurrent customer object (Rivo + Shopify data)
rivo_aw_settingsAccount widget settings (feature toggles, text, URLs)
rivo_aw_translationActive translation object
loy_configFull loyalty config
iconsSVG icon registry
rivo_recently_viewed_product_idsPersisted product IDs
rivo_recently_viewed_collectionsPersisted collection data
rivo_completed_preference_idsPersisted preferences

Example (JS):

const customer = RivoProfileAlpine.store("rivo_profile_customer");
console.log(customer.email);

Custom JavaScript Hooks

The Account Widget exposes built‑in JavaScript hook points via settings.

These hooks are configured in account_widget_settings and executed inside the widget when triggered.

1) onload_javascript

Runs once after the widget initializes.

Example:

// account_widget_settings.onload_javascript
const customer = RivoProfileAlpine.store("rivo_profile_customer");
if (customer?.tags?.includes("VIP")) {
  RivoProfileAlpine.store("rivo_aw_settings").home_logged_out_title = "Welcome back, VIP";
}

2) on_hash_change_javascript

Runs whenever the widget navigates to a new hash page.

Example:

// account_widget_settings.on_hash_change_javascript
// `page` and `id` are available as local variables
if (page === "rivo-orders") {
  console.log("User opened Orders page");
}

3) refresh_cart_javascript

Runs after the widget adds items to cart (and sometimes after gift card purchase).

Example:

// account_widget_settings.refresh_cart_javascript
document.dispatchEvent(new CustomEvent("cart:refresh"));

4) home_button_gift_card_click_action

Runs when the Gift Card CTA is clicked.

Example:

// account_widget_settings.home_button_gift_card_click_action
window.location.href = "/products/gift-card";

5) Login Event Hook (rivo-accounts:shopify:login)

When a customer logs in through the widget, a custom event is dispatched on document.

Event payload:

document.dispatchEvent(new CustomEvent('rivo-accounts:shopify:login', {
  detail: { customerId: response.customer?.id || response.customer?.remote_id }
}));

Listen for it:

document.addEventListener('rivo-accounts:shopify:login', (event) => {
  const { customerId } = event.detail || {};
  console.log("Rivo customer logged in:", customerId);

  // Example: refresh UI, analytics, or personalization
  document.dispatchEvent(new CustomEvent("loyalty:customer:logged_in", { detail: { customerId } }));
});

Deep Linking and Hash Routing

The widget uses window.location.hash to control pages. You can link directly to a widget page by setting the hash.

Base Pages

  • #rivo
  • #rivo-orders
  • #rivo-profile
  • #rivo-preferences
  • #rivo-favorites
  • #rivo-saved
  • #rivo-shipping-addresses
  • #rivo-add-shipping-address
  • #rivo-buy-gift-card
  • #rivo-gift-card-balance
  • #rivo-alternate-login
  • #rivo-manage-membership
  • #rivo-update-payment-method
  • #rivo-community

Pages With IDs

  • Order details: #rivo-order-details--ORDER_ID
  • Update shipping address: #rivo-update-shipping-address--ADDRESS_ID
  • Survey: #rivo-survey--SURVEY_HANDLE--QUESTION_ID
  • Gift card lookup: #rivo-lookup-gift-card--GIFT_CARD_UID
  • Favorite collection: #rivo-favorite-collection--COLLECTION_ID

Auto‑open

You can open the widget automatically by adding:

?show_rivo_account=true

Link Replacement (Account Redirects)

When replace_account_links is enabled, the widget will replace /account links with the widget hash.

To exclude a link from being replaced, add:

<a href="/account" data-rivo-accounts-ignore>Account</a>

You can also customize the replacement URL via:

  • replace_account_links_url (example: /pages/account#rivo)

Customizing UI Text & Content

The widget reads most copy from:

  • rivo_aw_translation (locale-specific)
  • rivo_aw_settings (fallback/defaults)

This means you can override text or insert HTML in translations and settings:

<div rivo-ax-html="$store.rivo_aw_translation.featured_home_content"></div>

Example settings commonly used:

  • featured_home_content
  • custom_sub_cta_content
  • home_cta_1_text, home_cta_1_url (and 2/3 variants)
  • home_logged_out_title
  • home_logged_out_subtitle
  • primary_button_class_names
  • secondary_button_class_names

Adding Custom Blocks to the Homepage

You can inject fully custom HTML blocks into the Account Widget homepage using the onload_javascript hook combined with Alpine-powered markup. This lets you display personalized content, promotions, or custom UI that reacts to customer data.

Important: All custom class names should be prefixed with rivo- to avoid conflicts with theme styles. CSS selectors should be scoped under #rivo-account-slideout to ensure styles only apply within the widget.

Step 1: Create Your HTML Block

Design your custom block using standard HTML with Rivo's Alpine directives (rivo-ax-*). The block can access any Alpine store data.

<div id="rivo-my-custom-block" class="rivo-custom-block" style="display: none;">
  <div class="rivo-custom-block-inner">
    <!-- Personalized greeting -->
    <h3 rivo-ax-text="'Welcome back, ' + ($store.rivo_profile_customer.first_name || 'friend') + '!'"></h3>

    <!-- Points display -->
    <div class="rivo-points-summary">
      <span>You have </span>
      <strong rivo-ax-text="$store.rivo_profile_customer.pretty_points_tally || '0'"></strong>
      <span> points</span>
    </div>

    <!-- Conditional VIP badge -->
    <div class="rivo-vip-badge" rivo-ax-show="$store.rivo_profile_customer.current_vip_tier?.name">
      <span rivo-ax-text="$store.rivo_profile_customer.current_vip_tier?.name + ' Member'"></span>
    </div>

    <!-- Points to next reward -->
    <p class="rivo-next-reward" rivo-ax-show="$store.rivo_profile_customer.points_to_next_reward > 0">
      <span rivo-ax-text="$store.rivo_profile_customer.points_to_next_reward"></span> points until your next reward!
    </p>
  </div>
</div>

Step 2: Inject Using the Onload Hook

Use onload_javascript to insert your block into the widget DOM and initialize it with Alpine:

// account_widget_settings.onload_javascript

// Wait for the widget home container to exist
const waitForHome = setInterval(() => {
  const homeContainer = document.querySelector('#rivo-account-slideout .rivo-home-page');

  if (!homeContainer) return;
  clearInterval(waitForHome);

  // Create the custom block
  const customBlock = document.createElement('div');
  customBlock.innerHTML = `
    <div id="rivo-my-custom-block" class="rivo-custom-block">
      <div class="rivo-custom-block-inner">
        <h3 rivo-ax-text="'Welcome back, ' + ($store.rivo_profile_customer.first_name || 'friend') + '!'"></h3>
        <div class="rivo-points-summary">
          <span>You have </span>
          <strong rivo-ax-text="$store.rivo_profile_customer.pretty_points_tally || '0'"></strong>
          <span> points</span>
        </div>
        <div class="rivo-vip-badge" rivo-ax-show="$store.rivo_profile_customer.current_vip_tier?.name">
          <span rivo-ax-text="$store.rivo_profile_customer.current_vip_tier?.name + ' Member'"></span>
        </div>
      </div>
    </div>
  `;

  // Insert at the top of the home container
  homeContainer.prepend(customBlock.firstElementChild);
}, 100);

Step 3: Add Custom Styles

Include CSS to style your block. You can add this via theme CSS or inline in the hook:

// Add styles dynamically in onload_javascript
const styles = document.createElement('style');
styles.textContent = `
  #rivo-account-slideout .rivo-custom-block {
    background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
    border-radius: 12px;
    padding: 20px;
    margin-bottom: 16px;
    color: white;
  }
  #rivo-account-slideout .rivo-custom-block h3 {
    margin: 0 0 12px 0;
    font-size: 1.25rem;
  }
  #rivo-account-slideout .rivo-points-summary {
    font-size: 1.1rem;
    margin-bottom: 8px;
  }
  #rivo-account-slideout .rivo-points-summary strong {
    font-size: 1.5rem;
  }
  #rivo-account-slideout .rivo-vip-badge {
    display: inline-block;
    background: rgba(255,255,255,0.2);
    padding: 4px 12px;
    border-radius: 20px;
    font-size: 0.85rem;
    margin-top: 8px;
  }
`;
document.head.appendChild(styles);

Complete Example: Promotional Block

Here's a full example that shows a promotional block only to customers who qualify:

// account_widget_settings.onload_javascript

// Add styles (scoped to #rivo-account-slideout)
const styles = document.createElement('style');
styles.textContent = `
  #rivo-account-slideout .rivo-promo-block {
    background: #1a1a2e;
    border: 1px solid #4a4a6a;
    border-radius: 12px;
    padding: 20px;
    margin-bottom: 16px;
    color: #fff;
  }
  #rivo-account-slideout .rivo-promo-block__title {
    font-size: 1.1rem;
    font-weight: 600;
    margin-bottom: 8px;
  }
  #rivo-account-slideout .rivo-promo-block__points {
    font-size: 2rem;
    font-weight: 700;
    color: #ffd700;
  }
  #rivo-account-slideout .rivo-promo-block__cta {
    margin-top: 12px;
  }
  #rivo-account-slideout .rivo-promo-block__cta a {
    background: #ffd700;
    color: #1a1a2e;
    padding: 8px 16px;
    border-radius: 6px;
    text-decoration: none;
    font-weight: 600;
  }
`;
document.head.appendChild(styles);

// Wait for home page
const waitForHome = setInterval(() => {
  const homeContainer = document.querySelector('#rivo-account-slideout .rivo-home-page');
  if (!homeContainer) return;
  clearInterval(waitForHome);

  const customer = RivoProfileAlpine.store('rivo_profile_customer');

  // Only show to logged-in customers with 500+ points
  if (!customer?.id || (customer?.points_tally || 0) < 500) return;

  const promoBlock = document.createElement('div');
  promoBlock.className = 'rivo-promo-block';
  promoBlock.innerHTML = `
    <div class="rivo-promo-block__title">
      <span rivo-ax-text="$store.rivo_profile_customer.first_name"></span>, you're so close!
    </div>
    <div>
      <span class="rivo-promo-block__points" rivo-ax-text="$store.rivo_profile_customer.pretty_points_tally"></span>
      <span> points earned</span>
    </div>
    <div class="rivo-promo-block__cta" rivo-ax-show="$store.rivo_profile_customer.points_tally >= 1000">
      <a href="#rivo-rewards">Redeem a Reward</a>
    </div>
  `;

  homeContainer.prepend(promoBlock);
}, 100);

Available Customer Fields for Custom Blocks

Use these fields from $store.rivo_profile_customer in your blocks:

FieldTypeDescription
first_namestringCustomer's first name
last_namestringCustomer's last name
emailstringCustomer's email
points_tallynumberRaw points balance
pretty_points_tallystringFormatted points (e.g., "1,250")
credits_tallynumberStore credit balance
pretty_credits_tallystringFormatted credit (e.g., "$12.50")
current_vip_tierobjectCurrent VIP tier (name, id)
points_to_next_rewardnumberPoints needed for next reward
referral_codestringCustomer's referral code
tagsarrayShopify customer tags
orders_countnumberTotal order count
total_spentstringLifetime spend

Tips for Custom Blocks

  • Always use rivo-ax-* – The widget uses a custom Alpine prefix to avoid conflicts
  • Use optional chaining – Customer data may not exist: $store.rivo_profile_customer?.first_name
  • Check login state – Use $store.rivo_profile_customer?.id to verify logged-in status
  • Wait for the container – Use setInterval to wait for the widget DOM to render

Utility Globals

The widget defines a few global helpers you can call:

window.rivoRefreshBalance();
window.rivoRefreshPointsLogs();

These re-fetch customer balances and activity logs.


Best Practices

  • Use the rivo-ax- prefix for all Alpine directives.
  • Always include fallbacks: customer?.first_name || 'there'.
  • Avoid heavy synchronous work in hooks.
  • Keep injected HTML minimal and safe.
  • Prefer pretty_points_tally / pretty_credits_tally for display.

Troubleshooting

Alpine directive does nothing

  • Make sure you used rivo-ax-, not x-.

Customer fields are missing

  • Customer data loads only when logged in.
  • Some fields are optional; use fallbacks.

Custom hook not firing

  • Confirm the setting is enabled in account_widget_settings.
  • Check for JS errors in the browser console.

FAQ

Can I use rivo_profile_customer outside the widget?
No. It only exists inside Account Widget pages. Use the Rivo JS SDK or API elsewhere.

How do I test custom hooks?
Add temporary console.log statements and open the widget to confirm runtime behavior.


Reference: Customer Store

For a full customer field reference, see the advanced customization guide or inspect:

console.log(RivoProfileAlpine.store("rivo_profile_customer"));

Customizing Outside of the Rivo Account Widget

The Rivo SDK isn't limited to customizing the Account Widget itself. You can use Rivo's customer data to personalize any part of your storefront page—announcement bars, hero sections, promotional banners, or any other element.

How It Works

  1. Access customer data via RivoProfileAlpine.store("rivo_profile_customer")
  2. Select any DOM element on your page using standard selectors
  3. Update content dynamically based on customer status, order history, or loyalty data

Key Data Sources

SourceDescriptionAvailable
RivoProfileAlpine.store("rivo_profile_customer")Full customer object with Rivo + Shopify dataAfter widget loads
RivoProfileAlpine.store("rivo_aw_settings")Widget settings and feature togglesAfter widget loads
RivoProfileAlpine.store("loy_config")Loyalty program configurationAfter widget loads
localStorage.getItem('rivo_profile_hint')Cached profile data (even logged out)After first identification
rivo:profile-hint-identified eventFires when profile hint is setOn page load

Accessing Customer Data

Via JavaScript:

// Get the customer store
const customer = RivoProfileAlpine.store("rivo_profile_customer");

// Access customer properties
console.log(customer.email);
console.log(customer.first_name);
console.log(customer.points_tally);
console.log(customer.pretty_points_tally);
console.log(customer.current_vip_tier?.name);

Via HTML with Alpine directives:

You can also bind customer data directly in your HTML using the rivo-ax- prefix and $store:

<!-- Display customer name -->
<span rivo-ax-text="$store.rivo_profile_customer.first_name"></span>

<!-- Display points balance -->
<p>You have <strong rivo-ax-text="$store.rivo_profile_customer.pretty_points_tally"></strong> points</p>

<!-- Conditionally show content for logged-in customers -->
<div rivo-ax-show="$store.rivo_profile_customer.id">
  Welcome back, <span rivo-ax-text="$store.rivo_profile_customer.first_name"></span>!
</div>

<!-- Show VIP tier badge if customer has one -->
<span class="vip-badge" rivo-ax-show="$store.rivo_profile_customer.current_vip_tier?.name"
      rivo-ax-text="$store.rivo_profile_customer.current_vip_tier?.name + ' Member'"></span>

<!-- Display credit balance -->
<div rivo-ax-show="$store.rivo_profile_customer.credits_tally > 0">
  Store credit: <span rivo-ax-text="$store.rivo_profile_customer.pretty_credits_tally"></span>
</div>

Note: Elements using rivo-ax- directives must be on pages where the Rivo Account Widget is loaded. The Alpine instance (RivoProfileAlpine) initializes when the widget loads.

Quick Start Example

This example shows how to personalize your page by replacing a hero title and injecting a VIP tier badge.

Where to add this code: Accounts → Account Widget → Javascript Settings for Developers → Onload Javascript


// Wait for the widget to initialize
setTimeout(() => {
  const customer = RivoProfileAlpine.store("rivo_profile_customer");
  if (!customer?.id) return; // Exit if not logged in

  // 1. Replace hero title with personalized greeting
  const heroTitle = document.querySelector('.hero-container h1');
  if (heroTitle) {
    heroTitle.textContent = `Hello, ${customer.first_name || 'there'}!`;
  }

  // 2. Inject VIP tier section below the hero subheading
  const heroSubheading = document.querySelector('.hero-container .subheading');
  if (heroSubheading && customer.current_vip_tier?.name) {
    const tierSection = document.createElement('div');
    tierSection.className = 'rivo-vip-section';
    tierSection.innerHTML = `
      <p class="rivo-vip-message">You're a <strong>${customer.current_vip_tier.name}</strong> member</p>
    `;
    heroSubheading.insertAdjacentElement('afterend', tierSection);
  }
}, 500);

Tip: Adjust the selector (.hero-container h1, .hero-container .subheading) to match your theme's actual HTML structure.

Example: Campaign Configuration

Here's a pattern for running personalized campaigns across your entire storefront:

// Define your campaign configuration
window.Rivo.loyalty_campaign_enabled = true;
window.Rivo.loyalty_campaign_config = {
  discount_code: "LOYALTY20",
  desktop_announcement_text: "VIP Exclusive: Extra 20% off with code LOYALTY20",
  mobile_announcement_text: "VIP: 20% off. Code: LOYALTY20",
  sign_in_text: "an exclusive 20% discount",
  hero_title_customer: "Welcome back! Your exclusive offer awaits",
  hero_title_guest: "Sign in to unlock your VIP discount",
  hero_body: "As a valued customer, enjoy an extra 20% off your next order."
};

// Set up campaign state based on customer eligibility
function setCampaignConfig() {
  if (window.Rivo.loyalty_campaign_enabled) {
    const customer = RivoProfileAlpine.store("rivo_profile_customer");

    window.Rivo.campaign_config = {
      profile_hint: JSON.parse(localStorage.getItem('rivo_profile_hint') || '{}'),
      discount_code: window.Rivo.loyalty_campaign_config.discount_code,
      is_mobile: window.innerWidth < 768
    };

    // Check if customer is eligible (e.g., has previous orders)
    window.Rivo.campaign_config.discount_code_eligible = (
      window.Rivo.campaign_config?.profile_hint?.customer_orders_count > 0 ||
      customer?.orders_count > 0
    );
  } else {
    window.Rivo.campaign_config = {};
  }
}

setCampaignConfig();

Example: Personalized Announcement Bar

Update your theme's announcement bar with personalized messaging:

function updateAnnouncementBar() {
  const announcementText = document.querySelector('.announcement-bar__text');
  if (!announcementText) return;

  const customer = RivoProfileAlpine.store("rivo_profile_customer");
  const firstName = customer?.first_name ||
                    window.Rivo.campaign_config?.profile_hint?.first_name;

  // Add smooth transition
  announcementText.style.transition = 'opacity 0.3s ease-out';
  announcementText.style.opacity = '0';

  setTimeout(() => {
    if (customer?.id) {
      // Logged-in customer: show discount code
      const text = window.Rivo.campaign_config.is_mobile
        ? window.Rivo.loyalty_campaign_config.mobile_announcement_text
        : window.Rivo.loyalty_campaign_config.desktop_announcement_text;
      announcementText.innerHTML = `<strong>${text}</strong>`;
    } else {
      // Guest: prompt to sign in
      announcementText.innerHTML = `<strong>${firstName ? `${firstName}, ` : ''}<a href="#rivo">Sign in</a> to unlock ${window.Rivo.loyalty_campaign_config.sign_in_text}</strong>`;
    }
    announcementText.style.opacity = '1';
  }, 300);
}

// Run on eligible customers
if (window.Rivo.campaign_config.discount_code_eligible) {
  updateAnnouncementBar();
}

Example: Dynamic Hero Section

Personalize a hero section based on customer loyalty data:

function updateHeroSection() {
  const heroContainer = document.querySelector('.hero-section');
  if (!heroContainer) return;

  const customer = RivoProfileAlpine.store("rivo_profile_customer");
  if (!customer?.first_order_at) return;

  // Calculate days since first order
  const firstOrderDate = new Date(customer.first_order_at);
  const today = new Date();
  const diffDays = Math.floor(Math.abs(today - firstOrderDate) / (1000 * 60 * 60 * 24));
  const formattedDays = String(diffDays).padStart(3, '0');

  const firstName = customer.first_name;

  // Update hero content
  const header = heroContainer.querySelector('.hero-header');
  const subtext = heroContainer.querySelector('.hero-subtext');

  if (header) {
    header.textContent = `DAY ${formattedDays}`;
  }
  if (subtext) {
    subtext.textContent = `${firstName ? `Hey ${firstName}, ` : ''}Thanks for being a loyal customer for ${diffDays} days!`;
  }
}

// Wait for customer data to load
const checkCustomerInterval = setInterval(() => {
  const customer = RivoProfileAlpine.store("rivo_profile_customer");
  if (customer?.first_order_at) {
    updateHeroSection();
    clearInterval(checkCustomerInterval);
  }
}, 100);

// Clear after 20 seconds to prevent infinite loop
setTimeout(() => clearInterval(checkCustomerInterval), 20000);

Example: Conditional Content Based on VIP Tier

Show different content based on the customer's VIP tier:

function showTierContent() {
  const tierBanner = document.querySelector('.vip-tier-banner');
  if (!tierBanner) return;

  const customer = RivoProfileAlpine.store("rivo_profile_customer");
  const currentTier = customer?.current_vip_tier?.name;

  if (currentTier === 'Gold' || currentTier === 'Platinum') {
    tierBanner.innerHTML = `
      <div class="tier-message">
        <h3>${customer.first_name}, you've unlocked ${currentTier} benefits!</h3>
        <p>Enjoy free shipping and early access to sales.</p>
      </div>
    `;
    tierBanner.style.display = 'block';
  } else {
    tierBanner.style.display = 'none';
  }
}

Listening for Profile Identification

For logged-out visitors who have been previously identified, listen for the profile hint event:

document.addEventListener('rivo:profile-hint-identified', () => {
  console.log('Profile hint identified');
  setCampaignConfig();

  const customer = RivoProfileAlpine.store("rivo_profile_customer");
  const discountCodeEligible =
    window.Rivo.campaign_config?.profile_hint?.customer_orders_count > 0;

  if (!customer?.id && discountCodeEligible) {
    // Logged-out returning customer—show personalized content
    updateAnnouncementBar();
    updateHeroSection();
  }
});

Auto-Applying Discount Codes

Automatically apply a discount code for eligible customers:

function applyDiscountCode(code) {
  // Check if already applied (via cookie)
  const currentCode = document.cookie
    .split('; ')
    .find(row => row.startsWith('discount_code='))
    ?.split('=')[1];

  if (currentCode !== code) {
    // Shopify discount URL pattern
    fetch(`/discount/${code}`);
  }
}

if (window.Rivo.campaign_config.discount_code_eligible) {
  applyDiscountCode(window.Rivo.campaign_config.discount_code);
}

Best Practices for Page Customization

  • Use RivoProfileAlpine.store("rivo_profile_customer") – This is the primary way to access customer data
  • Use intervals with timeouts – Customer data may load asynchronously; always set a max wait time
  • Check for elements before modifying – Use if (!element) return; guards
  • Check login state with customer?.id – Use optional chaining to verify the customer is logged in
  • Add smooth transitions – Avoid jarring content changes with CSS transitions
  • Combine with profile hints – Use localStorage hints to personalize even before full login
  • Listen for Rivo eventsrivo:profile-hint-identified and rivo-accounts:shopify:login let you react to state changes
  • Scope selectors carefully – Use specific selectors to avoid unintended changes