
Product
Unify Your Security Stack with Socket Basics
A single platform for static analysis, secrets detection, container scanning, and CVE checksβbuilt on trusted open source tools, ready to run out of the box.
@liquidcommerce/elements-sdk
Advanced tools
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.
π Quick Integration
|
ποΈ Complete E-commerce
|
π¨ Customizable UI
|
β‘ Performance First
|
The fastest way to add e-commerce to your site is with auto-initialization - a single script tag that does everything.
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:
Now let's display products. Choose the method that fits your use case:
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
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
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
By default, you get a floating cart button. Here's how to customize it:
<nav>
<div id="header-cart"></div>
</nav>
<script
data-liquid-commerce-elements
data-token="YOUR_API_KEY"
data-env="production"
data-cart-id="header-cart"
data-show-cart-items
src="https://assets-elements.liquidcommerce.us/all/elements.js"
></script>
<script
data-liquid-commerce-elements
data-token="YOUR_API_KEY"
data-env="production"
data-hide-cart-floating-button
src="https://assets-elements.liquidcommerce.us/all/elements.js"
></script>
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=shipping
Use case: Email campaigns, social media ads, QR codes
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=SUMMER20
Use case: Promotional campaigns, influencer codes, affiliate links
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
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 logsHere'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-id="container-id"
data-show-cart-items
data-hide-cart-floating-button
<!-- Products (Method 1: Direct) -->
data-container-1="div-id"
data-product-1="identifier"
data-container-2="div-id"
data-product-2="identifier"
<!-- 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.
Need more control? Initialize programmatically for full access to the SDK API.
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-sdk
<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
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' });
β οΈ Important: This SDK is designed for browser environments only. It will not work in server-side rendering, Node.js, or other non-browser environments.
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.
const client = await Elements('YOUR_API_KEY', {
env: 'production', // Environment
debugMode: 'none', // Debug mode
customTheme: { }, // Theme overrides
proxy: { }, // Proxy configuration
promoTicker: [ ] // Promotional messages
});
env: 'production' // Live environment (default)
env: 'staging' // Pre-production testing
env: 'development' // Development with extra logging
env: 'local' // Local development
debugMode: 'none' // No debugging (production default)
debugMode: 'console' // Console logs only
debugMode: 'panel' // Visual debug panel + console logs
Note: Debug mode is automatically disabled in production environment for security.
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
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.
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.
Inject SDK components into your page containers.
await client.injectProductElement([
{ containerId: 'pdp-1', identifier: '00619947000020' },
{ containerId: 'pdp-2', identifier: '00832889005513' }
]);
Identifier types: UPC, product ID, or Salsify grouping ID
await client.injectCartElement('cart-container');
Use case: Dedicated cart page
await client.injectCheckoutElement('checkout-container');
Use case: Dedicated checkout page
await client.injectAddressElement('address-container');
Use case: Shipping address collection page
Create standalone UI elements that integrate with the SDK.
client.ui.cartButton('header-cart', true);
Parameters:
containerId
- Where to place the buttonshowItemsCount
- Show item count badge (optional)Use case: Header navigation, sidebar
client.ui.floatingCartButton(true);
Parameters:
showItemsCount
- Show item count badge (optional)Use case: Always-visible cart access (bottom-right corner)
Bind elements to auto-update with cart data:
// Show live subtotal
client.ui.cartSubtotal('cart-total-display');
// Show live item count
client.ui.cartItemsCount('cart-badge');
Example:
<nav>
<span>Cart: $<span id="cart-total-display">0.00</span></span>
<span>(<span id="cart-badge">0</span> items)</span>
</nav>
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)
await client.builder.injectProductElement(params);
await client.builder.injectCartElement(containerId);
await client.builder.injectCheckoutElement(containerId);
await client.builder.injectAddressElement(containerId);
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;
// 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);
// 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();
Notes:
setAddressByPlacesId
action, use the Google Places ID FindersetAddressManually
action automatically generates a Google Places API-formatted address string from the provided componentsAction Feedback: All actions provide feedback through events. Listen for success/failure events to handle results and provide user feedback.
// 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 { applied, 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 { attempted, 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();
// Control checkout visibility
actions.checkout.openCheckout();
actions.checkout.closeCheckout();
actions.checkout.toggleCheckout();
// Pre-fill customer information
actions.checkout.updateCustomerInfo({
firstName: 'John',
lastName: 'Doe',
email: 'john@example.com',
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 { applied, 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 { attempted, 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 { applied, 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 { attempted, 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.
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 });
});
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 changedlce: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 clearedlce: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 enteredlce:actions.checkout_billing_information_updated
- Billing info enteredlce:actions.address_updated
- Address information changedlce:actions.address_cleared
- Address removedSee docs/EVENTS.md
for complete event reference with all available fields and implementation examples.
The SDK provides a theming system that lets you match components to your brand.
const client = await Elements('YOUR_API_KEY', {
customTheme: {
global: {
theme: {
primaryColor: '#007bff',
accentColor: '#6c757d',
successColor: '#28a745',
errorColor: '#dc3545',
warningColor: '#ffc107',
defaultTextColor: '#212529',
selectedTextColor: '#ffffff',
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'
}
}
}
});
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'
}
}
}
customTheme: {
cart: {
theme: {
backgroundColor: '#ffffff'
},
layout: {
showQuantityCounter: true,
quantityCounterStyle: 'outlined',
drawerHeaderText: 'Your Cart',
goToCheckoutButtonText: 'Checkout'
}
}
}
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'
}
}
}
customTheme: {
address: {
theme: {
backgroundColor: '#ffffff'
}
}
}
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
The SDK provides a comprehensive personalization/engraving feature for products that support it. The personalization experience varies based on context:
When browsing products, customers can add personalization through an enhanced form that includes:
// 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);
}
});
In the cart, personalized items display:
// 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);
});
During checkout, personalized items show:
Design Decision: Editing personalization during checkout is intentionally disabled to prevent order processing complications. Customers must return to the cart to make changes.
Control personalization display through global configuration:
customTheme: {
global: {
layout: {
enablePersonalization: true,
personalizationText: 'Personalize your product',
personalizationCardStyle: 'outlined' // or 'filled'
}
}
}
Note: Personalization is automatically enabled for products that support it. The SDK handles all UI, validation, and state management.
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);
});
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.
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 { code, appliedAmount, remainingBalance } = event.detail.data;
console.log(`Applied $${appliedAmount}, Remaining: $${remainingBalance}`);
});
window.addEventListener('lce:actions.checkout_gift_card_failed', (event) => {
const { code, error } = event.detail.data;
console.log('Gift card failed:', error);
});
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 { code, discountAmount, newTotal } = event.detail.data;
console.log(`${code} saved $${discountAmount}! New total: $${newTotal}`);
});
window.addEventListener('lce:actions.cart_promo_code_failed', (event) => {
const { code, error } = event.detail.data;
console.log('Promo failed:', error.message);
});
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.
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}`);
});
Automatically displays alerts when a retailer has minimum purchase requirements. No configuration needed - the SDK handles this automatically based on retailer rules.
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.
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`);
});
The SDK includes several built-in services that work behind the scenes to provide a robust, production-ready experience.
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.
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'
}
}
}
The SDK automatically tracks user interactions and performance metrics:
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
});
});
The SDK includes a circuit breaker pattern to prevent cascading failures:
// Automatically handles API failures
// - After 5 consecutive failures, circuit opens
// - Requests fail fast without hitting the API
// - After 30 seconds, circuit half-opens
// - Next successful request closes the circuit
Circuit states:
This protects your site from slowdowns when the API is experiencing issues.
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
// - Personalization
Fingerprinting is handled automatically and requires no configuration.
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.
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 logs
Console 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:
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 trail
Components are created on-demand using a factory pattern:
// When you call:
await client.injectProductElement([
{ containerId: 'pdp-1', identifier: 'product-123' }
]);
// 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 pages
Core services are managed as singletons:
// These services are initialized once and reused:
// - ApiClient
// - Store
// - PubSub
// - Logger
// - Telemetry
// - CircuitBreaker
// - Fingerprint
// - Auth
// This ensures:
// - Consistent state
// - Efficient memory usage
// - No duplicate API calls
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>
);
}
<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>
'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'
});
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>;
}
<!-- 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>
<!-- 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>
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();
await client.injectCartElement('cart-container');
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);
}
}
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');
}
});
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');
}
}
}
// 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');
}
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');
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');
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', identifier: 'id1' },
{ containerId: 'p2', identifier: 'id2' },
{ containerId: 'p3', identifier: 'id3' }
]);
Use event delegation instead of multiple listeners:
// β Don't listen to every event
window.addEventListener('lce:actions.product_loaded', handler);
window.addEventListener('lce:actions.product_add_to_cart', handler);
window.addEventListener('lce:actions.cart_updated', handler);
// ... 20 more listeners
// β
Use consolidated listeners
window.elements.onAllActions((data, metadata) => {
switch (metadata.eventName) {
case 'lce:actions.product_loaded':
handleProductLoad(data);
break;
case 'lce:actions.product_add_to_cart':
handleAddToCart(data);
break;
case 'lce:actions.cart_updated':
handleCartUpdate(data);
break;
}
});
Load SDK after critical page content:
<!-- β Don't load SDK in <head> -->
<head>
<script src="https://assets-elements.liquidcommerce.us/all/elements.js"></script>
</head>
<!-- β
Load SDK with defer or at end of body -->
<head>
<script defer src="https://assets-elements.liquidcommerce.us/all/elements.js"></script>
</head>
<!-- Or -->
<body>
<!-- Your page content -->
<script src="https://assets-elements.liquidcommerce.us/all/elements.js"></script>
</body>
Auto-init is optimized for performance:
<!-- β
Auto-init is the fastest way for simple setups -->
<div data-lce-product="00619947000020"></div>
<div data-lce-product="00832889005513"></div>
<script
data-liquid-commerce-elements
data-token="YOUR_API_KEY"
data-env="production"
src="https://assets-elements.liquidcommerce.us/all/elements.js"
></script>
Don't repeatedly inject the same component:
// β Don't re-inject on every state change
function updateProduct() {
setProductId(newId);
await client.injectProductElement([{ containerId: 'pdp', identifier: newId }]);
}
// β
Components update automatically on state changes
// Just inject once
useEffect(() => {
client.injectProductElement([{ containerId: 'pdp', identifier: productId }]);
}, []); // Empty deps - inject once
// Product will auto-update when you call actions
await client.actions.cart.addProduct([...]);
// β Don't repeatedly fetch the same data
async function showCartTotal() {
const cart = await client.actions.cart.getDetails();
return cart.amounts.total;
}
// β
Use UI helpers that auto-update
client.ui.cartSubtotal('cart-total-display');
client.ui.cartItemsCount('cart-count-display');
// Or cache and listen to updates
let cachedCart = await client.actions.cart.getDetails();
window.addEventListener('lce:actions.cart_updated', (event) => {
cachedCart = event.detail.data.current;
});
Always use CDN in production for optimal caching:
// β
CDN (recommended)
<script src="https://assets-elements.liquidcommerce.us/all/elements.js"></script>
// β Don't self-host unless necessary
<script src="/static/elements.js"></script>
Simpler themes = faster rendering:
// β Don't override every style property
customTheme: {
global: {
colors: { /* 20 color overrides */ },
typography: { /* 15 typography overrides */ },
shadows: { /* 10 shadow overrides */ },
// ... 100 more overrides
}
}
// β
Override only what's necessary
customTheme: {
global: {
colors: {
primary: '#007bff',
secondary: '#6c757d'
}
}
}
Track SDK performance in production:
// Measure initialization time
const start = performance.now();
const client = await Elements('YOUR_API_KEY', { env: 'production' });
console.log(`SDK initialized in ${performance.now() - start}ms`);
// Track component load times
window.addEventListener('lce:actions.product_loaded', (event) => {
const { loadTime } = event.detail.metadata;
console.log(`Product loaded in ${loadTime}ms`);
// Send to analytics
analytics.track('SDK Component Load', {
component: 'product',
duration: loadTime
});
});
Before going live:
env: 'production'
debugMode: 'none'
(or omit)Production-ready init:
const client = await Elements('YOUR_PRODUCTION_API_KEY', {
env: 'production',
debugMode: 'none',
customTheme: { /* minimal overrides */ },
proxy: { baseUrl: 'https://yourdomain.com/api/proxy' }
});
π For debugging and troubleshooting: See docs/TROUBLESHOOTING.md
for comprehensive problem-solving guide.
Route API requests through your server to avoid ad blockers:
const client = await Elements('YOUR_API_KEY', {
env: 'production',
proxy: {
baseUrl: 'https://yourdomain.com/api/liquidcommerce',
headers: {
'X-Custom-Header': 'value'
}
}
});
The SDK automatically handles routing and required headers. See docs/PROXY.md
for complete proxy setup guide with Next.js examples.
π Complete Documentation:
This project uses Semantic Versioning. Two CDN environments are available:
https://assets-elements.liquidcommerce.us/all/elements.js
(stable)https://assets-elements.liquidcommerce.us/all/beta/elements.js
(pre-release)If you need help with your API key, environment selection, or implementation, contact your LiquidCommerce representative.
Built with β€οΈ by the LiquidCommerce Team
Actions Reference β’ Events Guide β’ Browser Support β’ Proxy Setup
FAQs
LiquidCommerce Elements SDK
We found that @liquidcommerce/elements-sdk demonstrated a healthy version release cadence and project activity because the last version was released less than a year ago.Β It has 7 open source maintainers collaborating on the project.
Did you know?
Socket for GitHub automatically highlights issues in each pull request and monitors the health of all your open source dependencies. Discover the contents of your packages and block harmful activity before you install or update your dependencies.
Product
A single platform for static analysis, secrets detection, container scanning, and CVE checksβbuilt on trusted open source tools, ready to run out of the box.
Product
Socket is launching experimental protection for the Hugging Face ecosystem, scanning for malware and malicious payload injections inside model files to prevent silent AI supply chain attacks.
Research
/Security News
The Socket Threat Research Team uncovered a coordinated campaign that floods the Chrome Web Store with 131 rebranded clones of a WhatsApp Web automation extension to spam Brazilian users.