@liquidcommerce/elements-sdk
v2.6.4
Published
LiquidCommerce Elements SDK
Readme
Add product, cart, and checkout experiences to any website with a few lines of code
📋 Table of Contents
- Overview
- Quick Start
- Advanced Usage
- Browser Support
- Configuration
- SDK Methods & API
- Actions
- Events
- Themes & Customization
- Features Deep Dive
- Core Capabilities
- Integration Patterns
- Error Handling
- Performance & Best Practices
- Proxy Configuration
- Documentation
- Versioning
- Support
🎯 Overview
The LiquidCommerce Elements SDK is a production-ready JavaScript library that enables partners to seamlessly integrate product displays, shopping carts, and checkout flows into any website. Built with performance and developer experience in mind.
✨ Key Features
🚀 Quick Integration
- Auto-initialization with data attributes
- Zero configuration setup
- CDN or NPM installation
- Works with any framework or vanilla JS
🛍️ Complete E-commerce
- Product display components
- Shopping cart with real-time updates
- Full checkout flow
- Address management
🎨 Customizable UI
- Comprehensive theme system
- Component-level styling
- Responsive design
- Modern, accessible components
⚡ Performance First
- ~150KB bundle size
- Zero runtime dependencies
- Lazy loading support
- Optimized for Core Web Vitals
🚀 Quick Start
The fastest way to add e-commerce to your site is with auto-initialization - a single script tag that does everything.
The Simplest Setup (30 seconds)
Add this single script tag to your page:
<script
data-liquid-commerce-elements
data-token="YOUR_API_KEY"
data-env="production"
src="https://assets-elements.liquidcommerce.us/all/elements.js"
></script>That's it! You now have:
- ✅ A floating cart button (bottom right)
- ✅ Full cart functionality
- ✅ Complete checkout flow
- ✅ Ready to add products
Adding Products to Your Page
Now let's display products. Choose the method that fits your use case:
Method 1: Direct Attributes (Best for static pages)
Add products directly in the script tag:
<div id="product-1"></div>
<div id="product-2"></div>
<script
data-liquid-commerce-elements
data-token="YOUR_API_KEY"
data-env="production"
data-container-1="product-1"
data-product-1="00619947000020"
data-container-2="product-2"
data-product-2="00832889005513"
src="https://assets-elements.liquidcommerce.us/all/elements.js"
></script>Use case: Static HTML pages with known products
Method 2: JSON Configuration (Best for CMS/dynamic content)
Configure products via a JSON script block:
<div id="product-1"></div>
<div id="product-2"></div>
<script data-liquid-commerce-elements-products type="application/json">
[
{ "containerId": "product-1", "identifier": "00619947000020" },
{ "containerId": "product-2", "identifier": "00832889005513" }
]
</script>
<script
data-liquid-commerce-elements
data-token="YOUR_API_KEY"
data-env="production"
src="https://assets-elements.liquidcommerce.us/all/elements.js"
></script>Use case: CMS platforms, templating engines, server-side rendering
Method 3: Annotated Elements (Best for grids/lists)
Mark any div with a data attribute:
<div class="product-grid">
<div data-lce-product="00619947000020"></div>
<div data-lce-product="00832889005513"></div>
<div data-lce-product="00851468007252"></div>
</div>
<script
data-liquid-commerce-elements
data-token="YOUR_API_KEY"
data-env="production"
src="https://assets-elements.liquidcommerce.us/all/elements.js"
></script>Use case: Product grids, category pages, search results
Adding a Product List
Display a filtered, paginated product catalog with infinite scroll. Perfect for category pages, catalog pages, and search results.
Add a product list to any page with a data attribute:
<div data-liquid-commerce-elements-products-list
data-card="standard"
data-rows="3"
data-columns="4"
data-filters="personalization,pre-order,delivery-options"
data-product-url="/product/{upc}">
</div>
<script
data-liquid-commerce-elements
data-token="YOUR_API_KEY"
data-env="production"
src="https://assets-elements.liquidcommerce.us/all/elements.js"
></script>Attributes:
data-liquid-commerce-elements-products-list- Enables product list on this divdata-card- Card variant:standard(default)data-rows- Number of rows per page (default:3)data-columns- Number of columns (default:4)data-card-fill- Makes cards fill available space (optional flag)data-filters- Comma-separated filters:personalization,pre-order,delivery-optionsdata-product-url- Product URL template with{upc}or{grouping}placeholder
Use case: Category pages, catalog pages, search results, filtered product browsing
Customizing the Cart Button
By default, you get a floating cart button with badge. Here's how to customize it:
Option 1: Cart Button in a Specific Container
<nav>
<div id="header-cart"></div>
</nav>
<script
data-liquid-commerce-elements
data-token="YOUR_API_KEY"
data-env="production"
data-cart-badge-button="header-cart"
src="https://assets-elements.liquidcommerce.us/all/elements.js"
></script>Position Options:
You can control where the cart button is placed relative to a target element:
<!-- Place inside the target (default) -->
<script data-cart-badge-button="header-cart" ...></script>
<!-- Place above the target -->
<script data-cart-badge-button="above:.header-logo" ...></script>
<!-- Place below the target -->
<script data-cart-badge-button="below:#main-nav" ...></script>
<!-- Replace the target -->
<script data-cart-badge-button="replace:.old-cart" ...></script>ID Auto-Prefixing:
Element IDs are automatically prefixed with # if needed:
<!-- These are equivalent: -->
<script data-cart-button="header-cart" ...></script>
<script data-cart-button="#header-cart" ...></script>Option 2: Floating Cart Button (Default)
If no cart button attribute is provided, or if the target element is not found, the SDK automatically falls back to a floating cart button (bottom-right corner):
<!-- Empty attribute = floating cart button without badge -->
<script data-cart-button="" ...></script>
<!-- Empty badge attribute = floating cart button with badge -->
<script data-cart-badge-button="" ...></script>
<!-- Or simply omit the attribute for floating button with badge -->
<script
data-liquid-commerce-elements
data-token="YOUR_API_KEY"
src="https://assets-elements.liquidcommerce.us/all/elements.js"
></script>Option 3: No Cart Button (Manual Control)
Hide the cart button completely when you want to manage cart access manually:
<script
data-liquid-commerce-elements
data-token="YOUR_API_KEY"
data-env="production"
data-cart-button-hidden
src="https://assets-elements.liquidcommerce.us/all/elements.js"
></script>Use case: When you have a custom cart implementation or want to trigger cart display programmatically using client.actions.cart.openCart()
Advanced Auto-Init Features
Add Product via URL (Marketing Links)
Enable "add to cart" via URL parameters for email campaigns and ads:
<script
data-liquid-commerce-elements
data-token="YOUR_API_KEY"
data-env="production"
data-product-param="lce_product"
data-product-fulfillment-type-param="lce_fulfillment"
src="https://assets-elements.liquidcommerce.us/all/elements.js"
></script>Now this URL auto-adds a product to cart:
https://yoursite.com/shop?lce_product=00619947000020&lce_fulfillment=shippingUse case: Email campaigns, social media ads, QR codes
Apply Promo Code via URL (Campaign Tracking)
Auto-apply promo codes from URL parameters:
<script
data-liquid-commerce-elements
data-token="YOUR_API_KEY"
data-env="production"
data-promo-code-param="lce_promo"
src="https://assets-elements.liquidcommerce.us/all/elements.js"
></script>Now this URL auto-applies a promo code:
https://yoursite.com/shop?lce_promo=SUMMER20Use case: Promotional campaigns, influencer codes, affiliate links
Promo Ticker (Rotating Promotions)
Display rotating promotional messages:
<script
data-liquid-commerce-elements
data-token="YOUR_API_KEY"
data-env="production"
data-promo-code="FREESHIP"
data-promo-text="Free Shipping Today Only!|Use code FREESHIP at checkout"
data-promo-separator="•"
data-promo-active-from="2025-01-01T00:00:00Z"
data-promo-active-until="2025-01-31T23:59:59Z"
src="https://assets-elements.liquidcommerce.us/all/elements.js"
></script>Use case: Time-sensitive promotions, holiday sales, flash deals
Debug Mode (Development)
Enable debug logging during development:
<script
data-liquid-commerce-elements
data-token="YOUR_API_KEY"
data-env="development"
data-debug-mode="console"
src="https://assets-elements.liquidcommerce.us/all/elements.js"
></script>Debug modes:
console- Logs to browser consolepanel- Shows visual debug panel + console logs- Not set - No debugging (production default)
Complete Auto-Init Reference
Here's every available data attribute:
<script
data-liquid-commerce-elements
<!-- Required -->
data-token="YOUR_API_KEY"
<!-- Environment -->
data-env="production|staging|development|local"
<!-- Cart Button -->
data-cart-button="container-id" <!-- Simple cart button (no badge) -->
data-cart-badge-button="container-id" <!-- Cart button with badge -->
data-cart-button-hidden <!-- Hide cart button completely -->
<!-- Cart Button with Position Prefixes -->
data-cart-button="above:.logo" <!-- Place above target -->
data-cart-badge-button="below:#header" <!-- Place below target -->
data-cart-button="inside:.nav" <!-- Place inside target (default) -->
data-cart-badge-button="replace:.old-cart" <!-- Replace target -->
<!-- Cart Button Floating (empty values) -->
data-cart-button="" <!-- Floating button without badge -->
data-cart-badge-button="" <!-- Floating button with badge -->
<!-- Products (Method 1: Direct) -->
data-container-1="div-id"
data-product-1="identifier"
data-container-2="div-id"
data-product-2="identifier"
<!-- Product List -->
<div data-liquid-commerce-elements-products-list
data-card="standard"
data-rows="3"
data-columns="4"
data-card-fill
data-filters="personalization,pre-order,delivery-options"
data-product-url="/product/{upc}">
</div>
<!-- URL Parameters -->
data-product-param="lce_product"
data-product-fulfillment-type-param="lce_fulfillment"
data-promo-code-param="lce_promo"
<!-- Promo Ticker -->
data-promo-code="CODE"
data-promo-text="Message 1|Message 2"
data-promo-separator="•"
data-promo-active-from="2025-01-01T00:00:00Z"
data-promo-active-until="2025-12-31T23:59:59Z"
<!-- Debugging (dev only) -->
data-debug-mode="console|panel"
src="https://assets-elements.liquidcommerce.us/all/elements.js"
></script>📖 For complete auto-init options: See docs/CONFIGURATION.md for all data attributes and configuration details.
🔧 Advanced Usage
Need more control? Initialize programmatically for full access to the SDK API.
Installation
CDN:
<script src="https://assets-elements.liquidcommerce.us/all/elements.js"></script>
<!-- Pin to specific version: -->
<script src="https://assets-elements.liquidcommerce.us/all/1.2.3/elements.js"></script>NPM:
npm install @liquidcommerce/elements-sdk
# or
pnpm add @liquidcommerce/elements-sdkProgrammatic Initialization
<script src="https://assets-elements.liquidcommerce.us/all/elements.js"></script>
<script>
(async () => {
const client = await window.Elements('YOUR_API_KEY', {
env: 'production',
debugMode: 'none',
customTheme: { /* theming overrides */ },
proxy: { /* proxy config */ }
});
// Inject components
const components = await client.injectProductElement([
{ containerId: 'pdp-1', identifier: '00619947000020' }
]);
// Create cart button
client.ui.cartButton('cart-container', true);
// Use actions API
await client.actions.cart.addProduct([{
identifier: '00619947000020',
fulfillmentType: 'shipping',
quantity: 1
}]);
})();
</script>NPM Import:
import { Elements } from '@liquidcommerce/elements-sdk';
const client = await Elements('YOUR_API_KEY', { env: 'production' });🌐 Browser Support
⚠️ Important: This SDK is designed for browser environments only. It will not work in server-side rendering, Node.js, or other non-browser environments.
Supported Browsers (2018+)
| Browser | Minimum Version | Released | |---------|----------------|----------| | Chrome | 66+ | April 2018 | | Firefox | 60+ | May 2018 | | Safari | 12+ | September 2018 | | Edge | 79+ (Chromium) | January 2020 | | Samsung Internet | 7.2+ | June 2018 |
📖 See docs/BROWSER_SUPPORT.md for detailed compatibility.
⚙️ Configuration
Basic Configuration
const client = await Elements('YOUR_API_KEY', {
env: 'production', // Environment
debugMode: 'none', // Debug mode
customTheme: { }, // Theme overrides
proxy: { }, // Proxy configuration
promoTicker: [ ] // Promotional messages
});Environment Options
env: 'production' // Live environment (default)
env: 'staging' // Pre-production testing
env: 'development' // Development with extra logging
env: 'local' // Local developmentDebug Modes
debugMode: 'none' // No debugging (production default)
debugMode: 'console' // Console logs only
debugMode: 'panel' // Visual debug panel + console logsNote: Debug mode is automatically disabled in production environment for security.
Custom Theme
Override default styles and layouts:
customTheme: {
global: {
theme: {
primaryColor: '#007bff',
accentColor: '#6c757d',
successColor: '#28a745',
errorColor: '#dc3545',
buttonCornerRadius: '8px',
cardCornerRadius: '12px',
headingFont: {
name: 'Inter',
weights: [600, 700]
},
paragraphFont: {
name: 'Inter',
weights: [400, 500]
}
},
layout: {
allowPromoCodes: true,
inputFieldStyle: 'outlined'
}
},
product: {
layout: {
showDescription: true,
addToCartButtonText: 'Add to Cart',
fulfillmentDisplay: 'carousel'
}
},
cart: {
layout: {
showQuantityCounter: true,
drawerHeaderText: 'Your Cart'
}
},
checkout: {
layout: {
allowGiftCards: true,
emailOptIn: { show: true, checked: false, text: 'Email me with news' },
smsOptIn: { show: true, checked: false, text: 'Text me with updates' }
}
}
}📖 For complete theming options: See docs/THEMING.md
Proxy Configuration
Route API requests through your server to avoid ad blockers:
proxy: {
baseUrl: 'https://yourdomain.com/api/proxy',
headers: {
'X-Custom-Auth': 'your-token'
}
}See docs/PROXY.md for implementation guide.
Promo Ticker
Display rotating promotional messages:
promoTicker: [
{
promoCode: 'FREESHIP',
text: ['Free Shipping Today!', 'Use code FREESHIP'],
separator: '•',
activeFrom: '2025-01-01T00:00:00Z',
activeUntil: '2025-12-31T23:59:59Z'
}
]📖 For all configuration options: See docs/CONFIGURATION.md for complete reference with TypeScript types.
📖 SDK Methods & API
Component Injection
Inject SDK components into your page containers. All injection methods return wrapper objects that provide component control.
Products
const components = await client.injectProductElement([
{ containerId: 'pdp-1', identifier: '00619947000020' },
{ containerId: 'pdp-2', identifier: '00832889005513' }
]);
// Returns: IInjectedComponent[] - Array of component wrappers
// components[0].rerender() - Rerender the first component
// components[0].getElement() - Get the container element
// components[0].getType() - Get component typeIdentifier types: UPC, product ID, or Salsify grouping ID
Cart
const component = await client.injectCartElement('cart-container');
// Returns: IInjectedComponent | null - Component wrapper or null if failed
// component.rerender() - Rerender the component
// component.getElement() - Get the container element
// component.getType() - Get component typeUse case: Dedicated cart page
Checkout
const component = await client.injectCheckoutElement('checkout-container');
// Returns: IInjectedComponent | null - Component wrapper or null if failed
// component.rerender() - Rerender the component
// component.getElement() - Get the container element
// component.getType() - Get component typeUse case: Dedicated checkout page
Address
const component = await client.injectAddressElement('address-container');
// Returns: IInjectedComponent | null - Component wrapper or null if failed
// component.rerender() - Rerender the component
// component.getElement() - Get the container element
// component.getType() - Get component typeUse case: Shipping address collection page
Product List
await client.injectProductList({
containerId: 'product-list-container',
rows: 3, // Number of rows per page
columns: 4, // Number of columns
cardVariant: 'standard', // Card style variant
fillCard: false, // Fill card to available space
filters: [ // Optional filters
'personalization', // Show personalized products
'pre-order', // Show pre-order products
'delivery-options' // Show delivery type filters
],
productUrl: '/product/{upc}' // Optional: Product detail page URL template
});Parameters:
containerId- Where to inject the product listrows- Number of rows to display per page (default:3)columns- Number of columns in the grid (default:4)cardVariant- Card display style:'standard'(default)fillCard- Whether cards should fill available space (default:false)filters- Array of filter types to enable:'personalization','pre-order','delivery-options'productUrl- Optional URL template for product links. Use{upc}or{grouping}placeholder
Features:
- ✅ Infinite scroll pagination
- ✅ Filter by personalization, pre-order, delivery options
- ✅ Address-aware (reloads when address changes)
- ✅ Loading states and error handling
- ✅ Automatic GTM tracking (
view_item_list,select_item) - ✅ Add to cart directly from cards
- ✅ Product links with tracking
Use case: Category pages, catalog pages, search results, filtered product browsing
Note: The product list automatically reloads when the address changes to show availability for the new location.
Access All Injected Components
// Get all injected components
const injectedComponents = client.getInjectedComponents();
// Access specific components by container ID
const productComponent = injectedComponents.get('product-container-1');
const cartComponent = injectedComponents.get('cart-container');
// Iterate through all components
injectedComponents.forEach((component, containerId) => {
console.log(`Container: ${containerId}, Type: ${component.getType()}`);
// Rerender specific components
if (component.getType() === 'product') {
component.rerender();
}
});
// Get all components of a specific type
const productComponents = Array.from(injectedComponents.values())
.filter(component => component.getType() === 'product');
// Rerender all components of a type
productComponents.forEach(component => component.rerender());Returns: Map<string, IInjectedComponent> - Map of container IDs to component wrappers
Use cases:
- Debugging and inspecting injected components
- Bulk operations on multiple components
- Component management and cleanup
UI Helpers
Create standalone UI elements that integrate with the SDK.
Cart Button (in container)
client.ui.cartButton('header-cart', true);Parameters:
containerId- Where to place the buttonshowItemsCount- Show item count badge (optional)
Use case: Header navigation, sidebar
Floating Cart Button
client.ui.floatingCartButton(true);Parameters:
showItemsCount- Show item count badge (optional)
Use case: Always-visible cart access (bottom-right corner)
Live Cart Data Display
Bind elements to auto-update with cart data:
// Show live subtotal
client.ui.cartSubtotal('cart-total-display');
// Show live item count (default behavior: hides when count is 0)
client.ui.cartItemsCount('cart-badge');
// Show live item count (always visible, even when 0)
client.ui.cartItemsCount('cart-badge', { hideZero: false });Parameters for cartItemsCount:
elementId(string) - ID of the element to updateoptions(object, optional) - Configuration options:hideZero(boolean, default:true) - Whentrue, element is hidden when cart count is 0. Whenfalse, element remains visible showing "0".
Example:
<nav>
<span>Cart: $<span id="cart-total-display">0.00</span></span>
<span>(<span id="cart-badge">0</span> items)</span>
</nav>
<script>
// Default behavior - badge hidden when cart is empty
client.ui.cartItemsCount('cart-badge');
// Always show count, even when 0
client.ui.cartItemsCount('cart-badge', { hideZero: false });
</script>Builder Methods (Development Mode)
When isBuilder: true is set, additional methods are available for theme customization:
const client = await Elements('YOUR_API_KEY', {
env: 'development',
isBuilder: true
});
// Update component themes
await client.builder.updateComponentGlobalConfigs(globalTheme);
await client.builder.updateProductComponent(productTheme);
client.builder.updateCartComponent(cartTheme);
client.builder.updateCheckoutComponent(checkoutTheme);
client.builder.updateAddressComponent(addressTheme);
// Builder injection methods (same as regular methods)
const components = await client.builder.injectProductElement(params);
const component = await client.builder.injectCartElement(containerId);
const checkoutComponent = await client.builder.injectCheckoutElement(containerId);
const addressComponent = await client.builder.injectAddressElement(containerId);
// All return IInjectedComponent wrapper objects with rerender(), getElement(), getType() methods🎬 Actions
Actions provide programmatic control over SDK components. Access them via client.actions or window.elements.actions:
// Available after client initialization
const actions = client.actions;
// OR globally
const actions = window.elements.actions;Product Actions
// Get product details
const product = actions.product.getDetails('product-123');
console.log(product.name, product.brand, product.region, product.variety);
console.log(product.priceInfo, product.description, product.tastingNotes);Address Actions
// Set address using Google Places ID
await actions.address.setAddressByPlacesId('ChIJ0SRjyK5ZwokRp1TwT8dJSv8');
// Set address manually without Google Places (perfect for custom address forms)
await actions.address.setAddressManually(
{
one: '123 Main St',
two: 'Apt 4B', // Optional apartment/suite
city: 'New York',
state: 'NY',
zip: '10001',
country: 'United States' // Optional, will be included in formatted address
},
{
lat: 40.7505045,
long: -73.9934387
}
);
// Listen for success/failure via events
window.addEventListener('lce:actions.address_updated', function(event) {
const address = event.detail.data;
console.log('✅ Address set!', address.formattedAddress);
updateShippingOptions(address.coordinates);
});
window.addEventListener('lce:actions.address_failed', function(event) {
const error = event.detail.data;
console.log('❌ Address failed:', error.message);
showAddressForm();
});
// Get current address
const address = actions.address.getDetails();
// Clear saved address
actions.address.clear();Clear Address - Complete Reset
The actions.address.clear() action performs a comprehensive reset of the user's address and shopping session:
What it clears:
- ✅ Address Data: Removes all saved address information (street, city, state, zip, coordinates)
- ✅ Cart Contents: Completely resets the cart (removes all items, totals, promo codes)
- ✅ Local Storage: Completely removes the localStorage entry and its value
- ✅ Database: Deletes the persisted store from the server database
- ✅ Checkout State: Resets any pending checkout information
Why it resets the cart: When an address is cleared, the cart must be reset because:
- Cart items have location-specific pricing and availability
- Fulfillment options are tied to specific addresses
- Delivery fees and shipping costs depend on location
- Without a valid address, cart operations would fail or show incorrect data
Events fired:
lce:actions.address_cleared- Address successfully clearedlce:actions.cart_reset- Cart successfully reset
Use cases:
- Guest checkout option (clear previous user's data)
- Location change (start fresh with new address)
- Privacy compliance (complete data removal)
- Testing/development (reset to clean state)
Notes:
- To find Google Places IDs for the
setAddressByPlacesIdaction, use the Google Places ID Finder - The
setAddressManuallyaction automatically generates a Google Places API-formatted address string from the provided components - Manual addresses have an empty Places ID (as they don't come from Google Places API)
Action Feedback: All actions provide feedback through events. Listen for success/failure events to handle results and provide user feedback.
Cart Actions
// Control cart visibility
actions.cart.openCart();
actions.cart.closeCart();
actions.cart.toggleCart();
// Add products to cart
await actions.cart.addProduct([{
identifier: 'product-123',
fulfillmentType: 'shipping', // or 'onDemand'
quantity: 2
}]);
// Listen for add product feedback
window.addEventListener('lce:actions.cart_product_add_success', function(event) {
const { itemsAdded, identifiers } = event.detail.data;
console.log(`✅ Added ${itemsAdded} products to cart:`, identifiers);
showSuccessMessage('Products added to cart!');
});
window.addEventListener('lce:actions.cart_product_add_failed', function(event) {
const { identifiers, error } = event.detail.data;
console.log(`❌ Failed to add products:`, error);
showErrorMessage('Could not add products. Please try again.');
});
// Apply promo codes
await actions.cart.applyPromoCode('WELCOME10');
// Listen for promo code feedback
window.addEventListener('lce:actions.cart_promo_code_applied', function(event) {
const { discountAmount, newTotal } = event.detail.data;
console.log(`✅ Promo applied! Discount: $${discountAmount}, New total: $${newTotal}`);
showSavingsMessage(discountAmount);
});
window.addEventListener('lce:actions.cart_promo_code_failed', function(event) {
const { error } = event.detail.data;
console.log(`❌ Promo failed:`, error);
showErrorMessage('Promo code could not be applied');
});
// Remove promo codes
await actions.cart.removePromoCode();
// Get cart details
const cart = actions.cart.getDetails();
console.log(cart.itemCount, cart.amounts.total, cart.amounts.giftCardTotal);
// Reset cart
await actions.cart.resetCart();Checkout Actions
// Control checkout visibility
actions.checkout.openCheckout();
actions.checkout.closeCheckout();
actions.checkout.toggleCheckout();
// Pre-fill customer information
actions.checkout.updateCustomerInfo({
firstName: 'John',
lastName: 'Doe',
email: '[email protected]',
phone: '+1234567890'
});
// Pre-fill billing information
actions.checkout.updateBillingInfo({
firstName: 'John',
lastName: 'Doe',
street1: '123 Main St',
city: 'Anytown',
state: 'CA',
zipCode: '12345'
});
// Manage gift options
await actions.checkout.toggleIsGift(true);
actions.checkout.updateGiftInfo({
giftMessage: 'Happy Birthday!',
giftFrom: 'Your Friend'
});
// Apply discounts and gift cards
await actions.checkout.applyPromoCode('SAVE20');
await actions.checkout.applyGiftCard('GIFT123');
// Listen for checkout promo code feedback
window.addEventListener('lce:actions.checkout_promo_code_applied', function(event) {
const { discountAmount, newTotal } = event.detail.data;
console.log(`✅ Checkout promo applied! Saved: $${discountAmount}`);
updateCheckoutTotal(newTotal);
});
window.addEventListener('lce:actions.checkout_promo_code_failed', function(event) {
const { error } = event.detail.data;
console.log(`❌ Checkout promo failed:`, error);
showCheckoutError('Promo code could not be applied');
});
// Listen for gift card feedback
window.addEventListener('lce:actions.checkout_gift_card_applied', function(event) {
const { newTotal } = event.detail.data;
console.log('✅ Gift card applied successfully!');
updateCheckoutTotal(newTotal);
showSuccessMessage('Gift card applied to your order');
});
window.addEventListener('lce:actions.checkout_gift_card_failed', function(event) {
const { error } = event.detail.data;
console.log(`❌ Gift card failed:`, error);
showCheckoutError('Gift card could not be applied');
});
// Get checkout details (safe, non-sensitive data only)
const checkout = actions.checkout.getDetails();
console.log(checkout.itemCount, checkout.amounts.total, checkout.isGift);
console.log(checkout.hasAgeVerify, checkout.hasPromoCode, checkout.hasGiftCards);
console.log(checkout.acceptedAccountCreation, checkout.billingSameAsShipping);
console.log(checkout.marketingPreferences);
// Configure checkout options
await actions.checkout.toggleBillingSameAsShipping(true);
actions.checkout.toggleMarketingPreferences('canEmail', true);See docs/ACTIONS.md for complete action reference with business use cases.
📡 Events
The SDK emits real-time events for all user interactions. Listen to these events to trigger custom behavior:
// Listen for specific events
window.addEventListener('lce:actions.product_add_to_cart', function(event) {
const data = event.detail.data;
console.log('Added to cart:', data.identifier);
// Your custom logic here
analytics.track('Product Added', {
identifier: data.identifier,
quantity: data.quantity,
upc: data.upc
});
});
// Or use the helper methods (available after initialization)
window.elements.onAllForms((data, metadata) => {
console.log('Form Event', { data, metadata });
});
window.elements.onAllActions((data, metadata) => {
console.log('Action Event', { data, metadata });
});Available Events
Product Events
lce:actions.product_loaded- Product component loaded with comprehensive product details (region, country, abv, proof, age, variety, vintage, descriptions, tasting notes)lce:actions.product_add_to_cart- Item added to cartlce:actions.product_quantity_increase- Quantity increasedlce:actions.product_quantity_decrease- Quantity decreasedlce:actions.product_size_changed- Product size/variant changedlce:actions.product_fulfillment_type_changed- Delivery method changed
Cart Events
lce:actions.cart_loaded- Cart data loaded with complete cart information (itemCount, all amounts including giftCardTotal, detailed item data)lce:actions.cart_opened- Cart displayedlce:actions.cart_closed- Cart hiddenlce:actions.cart_updated- Cart contents changedlce:actions.cart_item_added- Item addedlce:actions.cart_item_removed- Item removedlce:actions.cart_reset- Cart cleared
Checkout Events
lce:actions.checkout_loaded- Checkout data loaded with comprehensive details (acceptedAccountCreation, hasSubstitutionPolicy, billingSameAsShipping, marketing preferences, detailed items)lce:actions.checkout_opened- Checkout startedlce:actions.checkout_closed- Checkout abandonedlce:actions.checkout_submit_started- Order submission beganlce:actions.checkout_submit_completed- Order completed successfullylce:actions.checkout_submit_failed- Order failedlce:actions.checkout_customer_information_updated- Customer info entered (returns boolean only, no sensitive data)lce:actions.checkout_gift_information_updated- Gift recipient info entered (returns boolean only, no sensitive data)lce:actions.checkout_billing_information_updated- Billing info entered (returns boolean only, no sensitive data)
Security Note: Form update events return only boolean: true to track completion without exposing sensitive customer information (names, emails, phone numbers, addresses, etc.). This protects your customers from malicious scripts and data breaches.
Address Events
lce:actions.address_updated- Address information changedlce:actions.address_cleared- Address removed
See docs/EVENTS.md for complete event reference with all available fields and implementation examples.
🎨 Themes & Customization
The SDK provides a theming system that lets you match components to your brand.
Global Theming
const client = await Elements('YOUR_API_KEY', {
customTheme: {
global: {
theme: {
primaryColor: '#007bff',
accentColor: '#6c757d',
successColor: '#28a745',
errorColor: '#dc3545',
warningColor: '#ffc107',
defaultTextColor: '#212529',
selectedTextColor: '#ffffff',
linkTextColor: '#1d4ed8',
drawerBackgroundColor: '#ffffff',
buttonCornerRadius: '8px',
cardCornerRadius: '12px',
headingFont: {
name: 'Inter',
weights: [600, 700]
},
paragraphFont: {
name: 'Inter',
weights: [400, 500]
}
},
layout: {
enablePersonalization: true,
personalizationText: 'Customize your product',
personalizationCardStyle: 'outlined',
allowPromoCodes: true,
inputFieldStyle: 'outlined',
poweredByMode: 'light'
}
}
}
});Component-Specific Theming
Product Component
customTheme: {
product: {
theme: {
backgroundColor: '#ffffff'
},
layout: {
showImages: true,
showTitle: true,
showDescription: true,
showQuantityCounter: true,
quantityCounterStyle: 'outlined',
fulfillmentDisplay: 'carousel',
enableShippingFulfillment: true,
enableOnDemandFulfillment: true,
addToCartButtonText: 'Add to Cart',
buyNowButtonText: 'Buy Now'
}
}
}Cart Component
customTheme: {
cart: {
theme: {
backgroundColor: '#ffffff'
},
layout: {
showQuantityCounter: true,
quantityCounterStyle: 'outlined',
drawerHeaderText: 'Your Cart',
goToCheckoutButtonText: 'Checkout'
}
}
}Checkout Component
customTheme: {
checkout: {
theme: {
backgroundColor: '#ffffff',
checkoutCompleted: {
customLogo: 'https://yourdomain.com/logo.png',
customText: 'Thank you for your order!'
}
},
layout: {
emailOptIn: {
show: true,
checked: false,
text: 'Email me with news'
},
smsOptIn: {
show: true,
checked: false,
text: 'Text me updates'
},
allowGiftCards: true,
drawerHeaderText: 'Checkout',
placeOrderButtonText: 'Place Order'
}
}
}Address Component
customTheme: {
address: {
theme: {
backgroundColor: '#ffffff'
}
}
}Dynamic Theme Updates (Builder Mode)
In development with isBuilder: true, update themes in real-time:
const client = await Elements('YOUR_API_KEY', {
env: 'development',
isBuilder: true
});
// Update global theme
await client.builder.updateComponentGlobalConfigs({
theme: { primaryColor: '#ff6b6b' }
});
// Update component-specific themes
await client.builder.updateProductComponent({
layout: { addToCartButtonText: 'Add to Bag' }
});
client.builder.updateCartComponent({
layout: { drawerHeaderText: 'Shopping Bag' }
});
client.builder.updateCheckoutComponent({
layout: { placeOrderButtonText: 'Complete Purchase' }
});
client.builder.updateAddressComponent({
theme: { backgroundColor: '#f8f9fa' }
});📖 For complete theming documentation: See docs/THEMING.md
🎁 Features Deep Dive
Product Personalization (Engraving)
The SDK provides a comprehensive personalization/engraving feature for products that support it. The personalization experience varies based on context:
Product View
When browsing products, customers can add personalization through an enhanced form that includes:
- Product information with pricing
- Fulfillment/retailer selection (with pricing comparison)
- Multi-line engraving inputs with character limits
- Real-time price updates as customers select different retailers
- Add-to-cart with personalization in one step
// Personalization appears automatically for engravable products
// Customers can add engraving text and select which retailer to fulfill from
// Listen for when personalization is added via add-to-cart
window.addEventListener('lce:actions.product_add_to_cart', (event) => {
const { hasEngraving, engravingLines } = event.detail.data;
if (hasEngraving) {
console.log('Customer personalized:', engravingLines);
}
});Cart View
In the cart, personalized items display:
- Personalization text lines
- Engraving fee (per item and total for quantity)
- Edit button to modify the personalization
- Remove button to remove personalization
// Customers can edit or remove engraving from cart items
window.addEventListener('lce:actions.cart_item_engraving_updated', (event) => {
const { identifier, engravingLines } = event.detail.data;
console.log('Cart item engraving updated:', engravingLines);
});Checkout View
During checkout, personalized items show:
- Personalization text lines (read-only)
- Engraving fee included in pricing
- Remove button only (editing not allowed in checkout)
Design Decision: Editing personalization during checkout is intentionally disabled to prevent order processing complications. Customers must return to the cart to make changes.
Theming & Configuration
Control personalization display through global configuration:
customTheme: {
global: {
layout: {
enablePersonalization: true,
personalizationText: 'Personalize your product',
personalizationCardStyle: 'outlined' // or 'filled'
}
}
}Key Features
- Smart pricing: Automatically includes engraving fees in product price
- Retailer selection: Compare prices from different retailers during personalization
- Character limits: Enforces maximum characters per line
- Uppercase conversion: Engraving text is automatically converted to uppercase
- Multi-line support: Products can support 1 or more engraving lines
- Fee transparency: Shows per-item and total fees (e.g., "$5.00 ($2.50 ea)")
Note: Personalization is automatically enabled for products that support it. The SDK handles all UI, validation, and state management.
Gift Options
Allow orders to be marked as gifts with custom messages:
// Enable via theme
customTheme: {
checkout: {
layout: {
allowGiftOptions: true
}
}
}
// Toggle gift mode programmatically
await client.actions.checkout.toggleIsGift(true);
// Set gift message
await client.actions.checkout.updateGiftInfo({
recipientName: 'John Doe',
message: 'Happy Birthday!'
});
// Listen for gift toggles
window.addEventListener('lce:actions.checkout_is_gift_toggled', (event) => {
const { isGift } = event.detail.data;
console.log('Order is gift:', isGift);
});Tips (On-Demand Delivery)
Allow customers to tip delivery drivers:
// Tips are automatically enabled for onDemand fulfillment types
// Listen for tip updates
window.addEventListener('lce:actions.checkout_tip_updated', (event) => {
const { tipAmount, total } = event.detail.data;
console.log(`Customer tipped $${tipAmount}`);
});Tips are calculated as a percentage or fixed amount and added to the order total.
Gift Cards
Accept gift card payments at checkout:
// Enable via theme
customTheme: {
checkout: {
layout: {
allowGiftCards: true
}
}
}
// Apply gift card programmatically
await client.actions.checkout.applyGiftCard('GIFT-1234-5678-9012');
// Remove gift card
await client.actions.checkout.removeGiftCard('GIFT-1234-5678-9012');
// Listen for gift card events
window.addEventListener('lce:actions.checkout_gift_card_applied', (event) => {
const { newTotal } = event.detail.data;
console.log(`Gift card applied! New total: $${newTotal}`);
});
window.addEventListener('lce:actions.checkout_gift_card_failed', (event) => {
const { error } = event.detail.data;
console.log('Gift card failed:', error);
});Promo Codes
Apply promotional discount codes:
// Apply to cart
await client.actions.cart.applyPromoCode('SUMMER20');
// Apply to checkout
await client.actions.checkout.applyPromoCode('SAVE10');
// Remove promo code
await client.actions.cart.removePromoCode();
// Listen for promo events
window.addEventListener('lce:actions.cart_promo_code_applied', (event) => {
const { discountAmount, newTotal } = event.detail.data;
console.log(`Promo applied! Saved $${discountAmount}! New total: $${newTotal}`);
});
window.addEventListener('lce:actions.cart_promo_code_failed', (event) => {
const { error } = event.detail.data;
console.log('Promo failed:', error);
});Promo Ticker
Display rotating promotional messages at the top of your site:
// Via initialization config
const client = await Elements('YOUR_API_KEY', {
promoTicker: [
{
promoCode: 'FREESHIP',
text: ['Free Shipping Today!', 'Use code FREESHIP'],
separator: '•',
activeFrom: '2025-01-01T00:00:00Z',
activeUntil: '2025-12-31T23:59:59Z'
},
{
promoCode: 'SAVE20',
text: ['20% Off Sitewide', 'Limited Time Only'],
separator: '|',
activeFrom: '2025-06-01T00:00:00Z',
activeUntil: '2025-06-30T23:59:59Z'
}
]
});
// Via auto-init
<script
data-liquid-commerce-elements
data-token="YOUR_API_KEY"
data-promo-code="FREESHIP"
data-promo-text="Free Shipping Today!|Use code FREESHIP"
data-promo-separator="•"
data-promo-active-from="2025-01-01T00:00:00Z"
data-promo-active-until="2025-12-31T23:59:59Z"
src="https://assets-elements.liquidcommerce.us/all/elements.js"
></script>The ticker automatically rotates messages and only shows active promotions.
Marketing Preferences
Allow customers to opt-in to email and SMS marketing:
// Set defaults via theme
customTheme: {
checkout: {
layout: {
emailOptIn: { checked: false, visible: true },
smsOptIn: { checked: false, visible: true }
}
}
}
// Update preferences programmatically
await client.actions.checkout.toggleMarketingPreferences('canEmail', true);
await client.actions.checkout.toggleMarketingPreferences('canSms', true);
// Listen for preference changes
window.addEventListener('lce:actions.checkout_marketing_preferences_toggled', (event) => {
const { field, value } = event.detail.data;
console.log(`Customer ${value ? 'opted-in' : 'opted-out'} of ${field}`);
});Purchase Minimum Alerts
Automatically displays alerts when a retailer has minimum purchase requirements. No configuration needed - the SDK handles this automatically based on retailer rules.
Age Verification
For age-restricted products (alcohol, tobacco, etc.), the SDK automatically displays age verification prompts during checkout. This is handled based on product metadata and cannot be disabled for restricted items.
Pre-Sale Countdown
For pre-sale or upcoming products, the SDK automatically displays a countdown timer until the product becomes available. Customers can add pre-sale items to cart, and the SDK handles the special fulfillment flow.
// Listen for when product is added to cart
window.addEventListener('lce:actions.product_add_to_cart', (event) => {
const { productId } = event.detail.data;
console.log(`Product ${productId} added to cart`);
});Product List
Display a filtered, paginated product catalog with infinite scroll. Perfect for category pages, catalog pages, and search results.
Auto-Initialization
Add a product list to any page with data attributes:
<div data-liquid-commerce-elements-products-list
data-card="standard"
data-rows="3"
data-columns="4"
data-filters="personalization,pre-order,delivery-options"
data-product-url="/product/{upc}">
</div>Programmatic Injection
await client.injectProductList({
containerId: 'product-list-container',
rows: 3,
columns: 4,
cardVariant: 'standard',
fillCard: false,
filters: ['personalization', 'pre-order', 'delivery-options'],
productUrl: '/product/{upc}'
});Features
Filtering:
- Personalization filter - Show only products that support engraving/personalization
- Pre-order filter - Show only pre-order products
- Delivery options filter - Filter by
all,shipping, oronDemand
Smart Filter Logic:
- When "Same-Day Delivery" is selected, personalization and pre-order filters are automatically disabled
- When personalization or pre-order is enabled, same-day delivery is automatically disabled
- Filters work together intelligently to show only compatible product combinations
Infinite Scroll:
- Automatically loads more products as you scroll
- Shows loading states during pagination
- Handles "no more products" gracefully
Address-Aware:
- Automatically reloads when address changes
- Shows availability based on current location
- Updates product pricing and fulfillment options dynamically
Product Cards:
- Display product image, name, size, and price
- Show availability status based on location
- "Add to Cart" button (disabled if unavailable)
- Clickable product links (if
productUrlis provided) - Presale product support with special handling
Analytics Tracking:
- Automatically tracks
view_item_listwhen products load - Tracks
select_itemwhen user clicks a product link - GTM integration for e-commerce events
Loading States:
- Shows skeleton loading cards during initial load
- Displays loading indicator during infinite scroll
- Error handling with user-friendly messages
Product URL Templates:
- Use
{upc}placeholder for UPC-based URLs:/product/{upc} - Use
{grouping}placeholder for Salsify grouping IDs:/product/{grouping} - Links open in new tab with proper security attributes
Example: Category Page
<div id="category-products"
data-liquid-commerce-elements-products-list
data-card="standard"
data-rows="4"
data-columns="4"
data-filters="personalization,delivery-options"
data-product-url="/products/{upc}">
</div>
<script
data-liquid-commerce-elements
data-token="YOUR_API_KEY"
data-env="production"
src="https://assets-elements.liquidcommerce.us/all/elements.js"
></script>Example: Search Results
const client = await Elements('YOUR_API_KEY', {
env: 'production'
});
await client.injectProductList({
containerId: 'search-results',
rows: 6,
columns: 3,
cardVariant: 'standard',
fillCard: true,
filters: ['delivery-options'],
productUrl: '/search?q={grouping}'
});Use cases:
- Category pages
- Catalog pages
- Search results
- Filtered product browsing
- Personalized product recommendations
- Pre-order product showcases
🔧 Core Capabilities
The SDK includes several built-in services that work behind the scenes to provide a robust, production-ready experience.
State Management
The SDK uses a centralized store for all state management. Access state data via actions:
// Get current cart state
const cart = await client.actions.cart.getDetails();
console.log(cart.itemCount, cart.amounts.total, cart.amounts.giftCardTotal);
// Get current checkout state
const checkout = await client.actions.checkout.getDetails();
console.log(checkout.amounts.total, checkout.isGift, checkout.acceptedAccountCreation);
console.log(checkout.billingSameAsShipping, checkout.marketingPreferences);
// Get current address
const address = await client.actions.address.getDetails();
console.log(address.formattedAddress, address.coordinates);
// Get product details
const product = await client.actions.product.getDetails('00619947000020');
console.log(product.name, product.region, product.variety, product.vintage);
console.log(product.description, product.tastingNotes);State is persistent: Cart and address data persist across page reloads using localStorage.
Event System (PubSub)
All SDK interactions emit events through a centralized event system:
// Subscribe to specific event
window.addEventListener('lce:actions.cart_updated', (event) => {
const { previous, current } = event.detail.data;
console.log('Cart changed from', previous.amounts.total, 'to', current.amounts.total);
});
// Subscribe to all action events
if (window.elements) {
window.elements.onAllActions((data, metadata) => {
console.log('Action:', metadata.eventName, data);
});
}
// Subscribe to all form events
if (window.elements) {
window.elements.onAllForms((data, metadata) => {
console.log('Form:', metadata.eventName, data);
});
}Event format:
{
detail: {
data: { /* event-specific payload */ },
metadata: {
eventName: 'lce:actions.cart_updated',
timestamp: 1699564800000,
source: 'sdk'
}
}
}Telemetry & Analytics
The SDK automatically tracks user interactions and performance metrics:
- User interactions: Add to cart, checkout started, checkout completed
- Performance metrics: Component load times, API response times
- Error tracking: Failed API calls, validation errors
Note: The SDK includes automatic Google Tag Manager (GTM) integration that tracks e-commerce events. GTM configuration is managed through your LiquidCommerce dashboard, not the client initialization.
Custom Analytics:
// Listen to events for custom analytics tracking
window.addEventListener('lce:actions.product_add_to_cart', (event) => {
const { productId, price, quantity } = event.detail.data;
// Track with your analytics provider
gtag('event', 'add_to_cart', {
currency: 'USD',
value: price * quantity,
items: [{ item_id: productId, quantity }]
});
// Or Segment
analytics.track('Product Added', {
product_id: productId,
price,
quantity
});
});Fingerprinting
The SDK generates a unique device fingerprint for fraud prevention and analytics:
// Automatically tracked:
// - Browser fingerprint
// - Device characteristics
// - Session information
// Used for:
// - Fraud detection
// - Cart persistence across devices
// - PersonalizationFingerprinting is handled automatically and requires no configuration.
Authentication
The SDK handles authentication automatically using your API key:
const client = await Elements('YOUR_API_KEY', {
env: 'production'
});
// All API requests include:
// - API key authentication
// - CORS headers
// - Request signing (when required)Token refresh: The SDK automatically handles token expiration and refresh.
Logger
Built-in logging system with configurable levels:
const client = await Elements('YOUR_API_KEY', {
debugMode: 'console' // 'none' | 'console' | 'panel'
});
// Debug mode options:
// - 'none': No logging (production default)
// - 'console': Logs to browser console
// - 'panel': Shows visual debug panel + console logsConsole debug output:
[LCE SDK] Product loaded: product-123
[LCE SDK] Cart updated: 3 items, $45.99
[LCE SDK] Checkout started
[LCE SDK] API call: POST /cart/add (152ms)Debug panel:
- Real-time event stream
- API call inspector
- State viewer
- Performance metrics
Command Pattern
The SDK uses a command pattern for all operations:
// Commands are:
// - Queued for execution
// - Retried on failure
// - Logged for debugging
// - Cancelable
// Example: Adding to cart
// 1. Command created: AddToCartCommand
// 2. Command queued
// 3. Command executed
// 4. Success event emitted
// 5. UI updated
// This ensures:
// - Consistent error handling
// - Automatic retries
// - Full audit trailComponent Factory
Components are created on-demand using a factory pattern:
// When you call:
const components = await client.injectProductElement([
{ containerId: 'pdp-1', identifier: 'product-123' }
]);
// Returns: IInjectedComponent[] - Array of component wrappers
// The SDK:
// 1. Creates a ProductComponent instance
// 2. Registers it with the factory
// 3. Injects it into the DOM
// 4. Attaches event listeners
// 5. Loads product data
// 6. Renders the component
// Components are:
// - Lazily loaded
// - Automatically cleaned up
// - Reusable across pagesSingleton Manager
Core services are managed as singletons:
// These services are initialized once and reused:
// - ApiClient
// - Store
// - PubSub
// - Logger
// - Telemetry
// - Fingerprint
// - Auth
// This ensures:
// - Consistent state
// - Efficient memory usage
// - No duplicate API calls🏗️ Integration Patterns
React Integration
import { useEffect, useState } from 'react';
import { Elements } from '@liquidcommerce/elements-sdk';
function ProductPage({ productId }) {
const [client, setClient] = useState(null);
useEffect(() => {
async function initSDK() {
const elementsClient = await Elements(process.env.REACT_APP_LCE_API_KEY, {
env: 'production'
});
setClient(elementsClient);
// Inject product
await elementsClient.injectProductElement([
{ containerId: 'product-container', identifier: productId }
]);
// Create cart button
elementsClient.ui.cartButton('cart-button', true);
}
initSDK();
// Cleanup
return () => {
// SDK handles cleanup automatically
};
}, [productId]);
return (
<div>
<div id="cart-button"></div>
<div id="product-container"></div>
</div>
);
}Vue Integration
<template>
<div>
<div ref="cartButton"></div>
<div ref="productContainer"></div>
</div>
</template>
<script>
import { Elements } from '@liquidcommerce/elements-sdk';
export default {
name: 'ProductPage',
props: ['productId'],
async mounted() {
this.client = await Elements(process.env.VUE_APP_LCE_API_KEY, {
env: 'production'
});
await this.client.injectProductElement([
{ containerId: this.$refs.productContainer.id, identifier: this.productId }
]);
this.client.ui.cartButton(this.$refs.cartButton.id, true);
}
}
</script>Next.js Integration
'use client';
import { useEffect } from 'react';
export default function ProductPage({ productId }: { productId: string }) {
useEffect(() => {
// Load SDK script
const script = document.createElement('script');
script.src = 'https://assets-elements.liquidcommerce.us/all/elements.js';
script.async = true;
script.onload = async () => {
const client = await (window as any).Elements(process.env.NEXT_PUBLIC_LCE_API_KEY, {
env: 'production'
});
const components = await client.injectProductElement([
{ containerId: 'product-container', identifier: productId }
]);
client.ui.floatingCartButton(true);
};
document.body.appendChild(script);
return () => {
document.body.removeChild(script);
};
}, [productId]);
return <div id="product-container"></div>;
}WordPress Integration
<!-- In your theme's header.php or functions.php -->
<script
data-liquid-commerce-elements
data-token="<?php echo get_option('lce_api_key'); ?>"
data-env="production"
src="https://assets-elements.liquidcommerce.us/all/elements.js"
></script>
<!-- In your product template -->
<div data-lce-product="<?php echo get_post_meta(get_the_ID(), 'product_upc', true); ?>"></div>Shopify Integration
<!-- In theme.liquid -->
<script
data-liquid-commerce-elements
data-token="{{ settings.lce_api_key }}"
data-env="production"
src="https://assets-elements.liquidcommerce.us/all/elements.js"
></script>
<!-- In product template -->
<div data-lce-product="{{ product.barcode }}"></div>Multi-Page Applications
For SPAs and multi-page apps, initialize once and reuse:
// app.js (initialize once)
let elementsClient = null;
async function getElementsClient() {
if (!elementsClient) {
elementsClient = await Elements('YOUR_API_KEY', {
env: 'production'
});
}
return elementsClient;
}
// product-page.js
const client = await getElementsClient();
await client.injectProductElement([
{ containerId: 'pdp-1', identifier: productId }
]);
// cart-page.js
const client = await getElementsClient();
const cartComponent = await client.injectCartElement('cart-container');🚨 Error Handling
Initialization Errors
try {
const client = await Elements('YOUR_API_KEY', {
env: 'production'
});
} catch (error) {
if (error.message.includes('Invalid API key')) {
console.error('Authentication failed');
} else if (error.message.includes('Network')) {
console.error('Network error - check connectivity');
} else {
console.error('SDK initialization failed:', error);
}
}Action Errors
All actions emit failure events with detailed error information:
// Product loaded successfully
window.addEventListener('lce:actions.product_loaded', (event) => {
const { identifier, productData } = event.detail.data;
console.log(`Product ${identifier} loaded successfully`);
});
// Cart action failure
window.addEventListener('lce:actions.cart_product_add_failed', (event) => {
const { identifiers, error } = event.detail.data;
console.error('Failed to add products:', error);
// Show user-friendly message
showNotification('Could not add to cart. Please try again.', 'error');
});
// Checkout failure
window.addEventListener('lce:actions.checkout_submit_failed', (event) => {
const { error, reason } = event.detail.data;
console.error('Checkout failed:', reason);
// Handle specific errors
if (reason.includes('payment')) {
showNotification('Payment declined. Please check your card details.', 'error');
} else if (reason.includes('inventory')) {
showNotification('Some items are no longer available.', 'warning');
} else {
showNotification('Checkout failed. Please try again.', 'error');
}
});
// Address validation failure
window.addEventListener('lce:actions.address_failed', (event) => {
const { error } = event.detail.data;
console.error('Address validation failed:', error);
showNotification('Please enter a valid address.', 'error');
});
// Promo code failure
window.addEventListener('lce:actions.cart_promo_code_failed', (event) => {
const { code, error } = event.detail.data;
console.error(`Promo code ${code} failed:`, error);
if (error.includes('expired')) {
showNotification('This promo code has expired.', 'warning');
} else if (error.includes('invalid')) {
showNotification('Invalid promo code.', 'error');
} else if (error.includes('minimum')) {
showNotification('Cart minimum not met for this promo.', 'warning');
}
});Network Error Recovery
let retryCount = 0;
const maxRetries = 3;
async function addProductWithRetry(productParams) {
try {
await client.actions.cart.addProduct(productParams);
} catch (error) {
if (retryCount < maxRetries && error.message.includes('Network')) {
retryCount++;
console.log(`Retrying... Attempt ${retryCount}/${maxRetries}`);
setTimeout(() => addProductWithRetry(productParams), 1000 * retryCount);
} else {
console.error('Failed after retries:', error);
showNotification('Network error. Please check your connection.', 'error');
}
}
}Global Error Handler
// Listen to all failed events
window.addEventListener('lce:actions.cart_failed', handleError);
window.addEventListener('lce:actions.checkout_failed', handleError);
window.addEventListener('lce:actions.address_failed', handleError);
window.addEventListener('lce:actions.cart_product_add_failed', handleError);
function handleError(event) {
const { error, context } = event.detail.data;
// Log to error tracking service
if (window.Sentry) {
Sentry.captureException(error, {
tags: { sdk: 'liquid-commerce' },
extra: context
});
}
// Show user notification
showNotification('Something went wrong. Please try again.', 'error');
}⚡ Performance & Best Practices
Lazy Loading Components
Only inject components when needed:
// ❌ Don't inject all components upfront
await client.injectProductElement([/* 50 products */]);
await client.injectCartElement('cart');
await client.injectCheckoutElement('checkout');
// ✅ Inject components as user navigates
// On product page:
await client.injectProductElement([{ containerId: 'pdp', identifier: productId }]);
// On cart page (when user clicks cart):
await client.injectCartElement('cart');
// On checkout (when user proceeds):
await client.injectCheckoutElement('checkout');Reuse Client Instance
Initialize once, use everywhere:
// ❌ Don't create multiple clients
// page1.js
const client1 = await Elements('KEY', { env: 'production' });
// page2.js
const client2 = await Elements('KEY', { env: 'production' });
// ✅ Create once, reuse
// app.js
window.lceClient = await Elements('KEY', { env: 'production' });
// page1.js
const client = window.lceClient;
await client.injectProductElement([...]);
// page2.js
const client = window.lceClient;
await client.injectCartElement('cart');Batch Product Injections
Group product injections together:
// ❌ Don't inject products one by one
await client.injectProductElement([{ containerId: 'p1', identifier: 'id1' }]);
await client.injectProductElement([{ containerId: 'p2', identifier: 'id2' }]);
await client.injectProductElement([{ containerId: 'p3', identifier: 'id3' }]);
// ✅ Inject all products at once
await client.injectProductElement([
{ containerId: 'p1', ide