@liquidcommerce/elements-sdk 2.7.0 → 2.7.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (81) hide show
  1. package/README.md +83 -2750
  2. package/dist/index.checkout.esm.js +6898 -6837
  3. package/dist/index.esm.js +11463 -10993
  4. package/dist/types/auto-initialize/shared-utils.d.ts +5 -0
  5. package/dist/types/constants/core.constant.d.ts +0 -4
  6. package/dist/types/core/base-component.service.d.ts +2 -1
  7. package/dist/types/core/pubsub/interfaces/checkout.interface.d.ts +1 -0
  8. package/dist/types/enums/core.enum.d.ts +11 -0
  9. package/dist/types/interfaces/configs/product-list.interface.d.ts +2 -2
  10. package/dist/types/interfaces/core.interface.d.ts +5 -4
  11. package/dist/types/modules/address/address-input.component.d.ts +11 -0
  12. package/dist/types/modules/checkout/components/checkout-stripe-form.component.d.ts +2 -1
  13. package/dist/types/modules/product-list/components/card-components/index.d.ts +3 -0
  14. package/dist/types/modules/product-list/components/card-components/product-badge.d.ts +8 -0
  15. package/dist/types/modules/product-list/components/card-components/product-fulfillments.d.ts +2 -0
  16. package/dist/types/modules/product-list/components/card-components/product-price-and-personalization.d.ts +13 -0
  17. package/dist/types/modules/product-list/components/card-components/product-title.d.ts +6 -0
  18. package/dist/types/modules/product-list/components/index.d.ts +1 -0
  19. package/dist/types/modules/product-list/components/product-list-engraving.component.d.ts +5 -1
  20. package/dist/types/modules/product-list/components/product-list-product-pre-cart.component.d.ts +28 -0
  21. package/dist/types/modules/product-list/components/product-list-retailers.component.d.ts +10 -2
  22. package/dist/types/modules/product-list/product-list-card.component.d.ts +0 -5
  23. package/dist/types/modules/product-list/product-list.commands.d.ts +11 -2
  24. package/dist/types/modules/product-list/product-list.interface.d.ts +1 -0
  25. package/dist/types/modules/ui-components/engraving/engraving-form.component.d.ts +11 -2
  26. package/dist/types/modules/ui-components/lce-element/lce-element.component.d.ts +2 -1
  27. package/dist/types/utils/dom-compat.d.ts +2 -0
  28. package/docs/gitbook/actions.md +964 -0
  29. package/docs/gitbook/address.md +48 -0
  30. package/docs/gitbook/cart.md +65 -0
  31. package/docs/gitbook/checkout.md +131 -0
  32. package/docs/gitbook/events.md +1765 -0
  33. package/docs/gitbook/overview.md +166 -0
  34. package/docs/gitbook/product.md +64 -0
  35. package/docs/gitbook/quick-start-guide.md +393 -0
  36. package/docs/v1/README.md +210 -0
  37. package/docs/v1/api/actions/address-actions.md +281 -0
  38. package/docs/v1/api/actions/cart-actions.md +337 -0
  39. package/docs/v1/api/actions/checkout-actions.md +387 -0
  40. package/docs/v1/api/actions/product-actions.md +115 -0
  41. package/docs/v1/api/client.md +482 -0
  42. package/docs/v1/api/configuration.md +1 -0
  43. package/docs/v1/api/injection-methods.md +247 -0
  44. package/docs/v1/api/typescript-types.md +1 -0
  45. package/docs/v1/api/ui-helpers.md +200 -0
  46. package/docs/v1/examples/advanced-patterns.md +96 -0
  47. package/docs/v1/examples/checkout-flow.md +91 -0
  48. package/docs/v1/examples/custom-theming.md +63 -0
  49. package/docs/v1/examples/multi-product-page.md +90 -0
  50. package/docs/v1/examples/simple-product-page.md +89 -0
  51. package/docs/v1/getting-started/concepts.md +507 -0
  52. package/docs/v1/getting-started/installation.md +328 -0
  53. package/docs/v1/getting-started/quick-start.md +405 -0
  54. package/docs/v1/guides/address-component.md +431 -0
  55. package/docs/v1/guides/best-practices.md +324 -0
  56. package/docs/v1/guides/cart-component.md +737 -0
  57. package/docs/v1/guides/checkout-component.md +672 -0
  58. package/docs/v1/guides/events.md +926 -0
  59. package/docs/v1/guides/product-component.md +686 -0
  60. package/docs/v1/guides/product-list-component.md +598 -0
  61. package/docs/v1/guides/theming.md +216 -0
  62. package/docs/v1/integration/angular.md +39 -0
  63. package/docs/v1/integration/laravel.md +41 -0
  64. package/docs/v1/integration/nextjs.md +60 -0
  65. package/docs/v1/integration/proxy-setup.md +89 -0
  66. package/docs/v1/integration/react.md +64 -0
  67. package/docs/v1/integration/vanilla-js.md +84 -0
  68. package/docs/v1/integration/vue.md +34 -0
  69. package/docs/v1/reference/browser-support.md +44 -0
  70. package/docs/v1/reference/error-handling.md +70 -0
  71. package/docs/v1/reference/performance.md +54 -0
  72. package/docs/v1/reference/troubleshooting.md +64 -0
  73. package/package.json +1 -1
  74. package/docs/ACTIONS.md +0 -1301
  75. package/docs/BROWSER_SUPPORT.md +0 -279
  76. package/docs/CONFIGURATION.md +0 -997
  77. package/docs/DOCUMENTATION_INDEX.md +0 -319
  78. package/docs/EVENTS.md +0 -798
  79. package/docs/PROXY.md +0 -228
  80. package/docs/THEMING.md +0 -681
  81. package/docs/TROUBLESHOOTING.md +0 -793
@@ -0,0 +1,686 @@
1
+ # Product Component
2
+
3
+ The Product component displays product information with add-to-cart functionality, image carousel, size selection, and fulfillment options.
4
+
5
+ ## Overview
6
+
7
+ The Product component automatically:
8
+ - Displays product images in a carousel
9
+ - Shows product name, description, and pricing
10
+ - Provides size selection
11
+ - Offers fulfillment type options (shipping/on-demand delivery)
12
+ - Handles retailer selection
13
+ - Includes add-to-cart functionality
14
+ - Supports product personalization/engraving
15
+ - Adjusts pricing based on delivery location
16
+
17
+ ## Basic Usage
18
+
19
+ ### Declarative Setup
20
+
21
+ The simplest way to add a product is using HTML data attributes:
22
+
23
+ ```html
24
+ <script
25
+ defer
26
+ data-liquid-commerce-elements
27
+ data-token="YOUR_API_KEY"
28
+ data-env="production"
29
+ data-container-1="product-display"
30
+ data-product-1="00619947000020"
31
+ type="text/javascript"
32
+ src="https://assets-elements.liquidcommerce.us/all/elements.js"
33
+ ></script>
34
+
35
+ <div id="product-display"></div>
36
+ ```
37
+
38
+ **Multiple products:**
39
+
40
+ ```html
41
+ <script
42
+ defer
43
+ data-liquid-commerce-elements
44
+ data-token="YOUR_API_KEY"
45
+ data-env="production"
46
+ data-container-1="product-1"
47
+ data-product-1="00619947000020"
48
+ data-container-2="product-2"
49
+ data-product-2="08504405135"
50
+ type="text/javascript"
51
+ src="https://assets-elements.liquidcommerce.us/all/elements.js"
52
+ ></script>
53
+
54
+ <div id="product-1"></div>
55
+ <div id="product-2"></div>
56
+ ```
57
+
58
+ ### Alternative: Annotated Elements
59
+
60
+ Use `data-lce-product` on any div:
61
+
62
+ ```html
63
+ <script
64
+ defer
65
+ data-liquid-commerce-elements
66
+ data-token="YOUR_API_KEY"
67
+ data-env="production"
68
+ type="text/javascript"
69
+ src="https://assets-elements.liquidcommerce.us/all/elements.js"
70
+ ></script>
71
+
72
+ <div data-lce-product="00619947000020"></div>
73
+ <div data-lce-product="08504405135"></div>
74
+ ```
75
+
76
+ The SDK automatically generates IDs and injects products into these elements.
77
+
78
+ ### Alternative: JSON Configuration
79
+
80
+ For many products, use a JSON script tag:
81
+
82
+ ```html
83
+ <script
84
+ defer
85
+ data-liquid-commerce-elements
86
+ data-token="YOUR_API_KEY"
87
+ data-env="production"
88
+ type="text/javascript"
89
+ src="https://assets-elements.liquidcommerce.us/all/elements.js"
90
+ ></script>
91
+
92
+ <script data-liquid-commerce-elements-products type="application/json">
93
+ [
94
+ { "containerId": "product-1", "identifier": "00619947000020" },
95
+ { "containerId": "product-2", "identifier": "08504405135" },
96
+ { "containerId": "product-3", "identifier": "08068660001" }
97
+ ]
98
+ </script>
99
+
100
+ <div id="product-1"></div>
101
+ <div id="product-2"></div>
102
+ <div id="product-3"></div>
103
+ ```
104
+
105
+ ### Programmatic Setup
106
+
107
+ Use the JavaScript API for dynamic product injection:
108
+
109
+ ```javascript
110
+ const client = await Elements('YOUR_API_KEY', {
111
+ env: 'production'
112
+ });
113
+
114
+ await client.injectProductElement([
115
+ { containerId: 'product-display', identifier: '00619947000020' }
116
+ ]);
117
+ ```
118
+
119
+ **With NPM:**
120
+
121
+ ```javascript
122
+ import { Elements } from '@liquidcommerce/elements-sdk';
123
+
124
+ const client = await Elements('YOUR_API_KEY', { env: 'production' });
125
+
126
+ await client.injectProductElement([
127
+ { containerId: 'product-1', identifier: '00619947000020' },
128
+ { containerId: 'product-2', identifier: '08504405135' }
129
+ ]);
130
+ ```
131
+
132
+ ## Product Identifiers
133
+
134
+ Products can be identified using:
135
+
136
+ - **UPC**: `00619947000020`
137
+ - **Salsify Grouping**: `GROUPING-12345`
138
+
139
+ The SDK automatically resolves any of these identifier types.
140
+
141
+ ## Features
142
+
143
+ ### Image Carousel
144
+
145
+ Products with multiple images display in an interactive carousel:
146
+
147
+ - Swipe/arrow navigation
148
+ - Thumbnail preview
149
+ - Lazy loading for performance
150
+
151
+ ### Size Selection
152
+
153
+ For products with multiple sizes:
154
+
155
+ - Size selector
156
+ - Price updates per size
157
+ - Availability checking per size
158
+ - Out-of-stock indication
159
+
160
+ ### Fulfillment Types
161
+
162
+ Two fulfillment options:
163
+
164
+ **Shipping**
165
+ - Standard delivery
166
+ - Nationwide availability
167
+ - Carrier-based shipping
168
+
169
+ **On-Demand Delivery**
170
+ - Same-day or scheduled delivery
171
+ - Local availability only
172
+ - Location-dependent pricing
173
+
174
+ The component shows only available fulfillment types based on the user's location.
175
+
176
+ ### Retailer Selection
177
+
178
+ For products with multiple retailers:
179
+
180
+ **Carousel View** (default)
181
+ - Swipeable carousel of retailer cards
182
+ - Shows retailer address, shipping/delivery expectation time, and pricing
183
+ - Select with one tap
184
+
185
+ **Popup View**
186
+ - "Choose Retailer" button
187
+ - Modal with full retailer list
188
+ - Filter and search capabilities
189
+
190
+ ### Personalization/Engraving
191
+
192
+ For products that support personalization:
193
+
194
+ - Engraving form appears automatically
195
+ - Character limits enforced
196
+ - Additional fees displayed
197
+
198
+ ### Quantity Selection
199
+
200
+ Adjust product quantity before adding to cart:
201
+
202
+ - Increment/decrement buttons
203
+ - Direct input field
204
+ - Inventory limits enforced
205
+
206
+ ## Actions API
207
+
208
+ Programmatically interact with products:
209
+
210
+ ### Get Product Details
211
+
212
+ Retrieve product information:
213
+
214
+ ```javascript
215
+ const productData = window.elements.actions.product.getDetails('00619947000020');
216
+
217
+ console.log(productData);
218
+ // {
219
+ // identifier: '00619947000020',
220
+ // name: 'Premium Whiskey',
221
+ // price: 4999,
222
+ // selectedSize: { id: '750ml', upc: '00619947000020', ... },
223
+ // selectedFulfillmentType: 'shipping',
224
+ // selectedRetailer: { id: 'retailer_123', name: 'Spirits Shop', ... },
225
+ // quantity: 1,
226
+ // ...
227
+ // }
228
+ ```
229
+
230
+ **Note:** The product must be injected and loaded before calling `getDetails()`. If the product hasn't been loaded, an error is thrown.
231
+
232
+ ## Events
233
+
234
+ Listen for product-related events:
235
+
236
+ ### Product Loaded
237
+
238
+ Fired when product data is successfully loaded:
239
+
240
+ ```javascript
241
+ window.addEventListener('lce:actions.product_loaded', (event) => {
242
+ const { identifier, name, price } = event.detail.data;
243
+ console.log(`Product loaded: ${name} - $${price / 100}`);
244
+ });
245
+ ```
246
+
247
+ ### Product Add to Cart
248
+
249
+ Fired when user clicks "Add to Cart":
250
+
251
+ ```javascript
252
+ window.addEventListener('lce:actions.product_add_to_cart', (event) => {
253
+ const { identifier, quantity, fulfillmentType } = event.detail.data;
254
+ console.log(`Adding ${quantity}x ${identifier} (${fulfillmentType})`);
255
+ });
256
+ ```
257
+
258
+ ### Size Changed
259
+
260
+ Fired when user selects a different size:
261
+
262
+ ```javascript
263
+ window.addEventListener('lce:actions.product_size_changed', (event) => {
264
+ const { identifier, selectedSize, price } = event.detail.data;
265
+ console.log(`Size changed to ${selectedSize.name}: $${price / 100}`);
266
+ });
267
+ ```
268
+
269
+ ### Fulfillment Type Changed
270
+
271
+ Fired when user switches between shipping and on-demand:
272
+
273
+ ```javascript
274
+ window.addEventListener('lce:actions.product_fulfillment_type_changed', (event) => {
275
+ const { identifier, fulfillmentType } = event.detail.data;
276
+ console.log(`Fulfillment type changed to: ${fulfillmentType}`);
277
+ });
278
+ ```
279
+
280
+ ### Fulfillment (Retailer) Changed
281
+
282
+ Fired when user selects a different retailer:
283
+
284
+ ```javascript
285
+ window.addEventListener('lce:actions.product_fulfillment_changed', (event) => {
286
+ const { identifier, selectedRetailer, price } = event.detail.data;
287
+ console.log(`Retailer changed to ${selectedRetailer.name}: $${price / 100}`);
288
+ });
289
+ ```
290
+
291
+ ### Quantity Increased/Decreased
292
+
293
+ Fired when user adjusts quantity:
294
+
295
+ ```javascript
296
+ window.addEventListener('lce:actions.product_quantity_increase', (event) => {
297
+ const { identifier, quantity } = event.detail.data;
298
+ console.log(`Quantity increased to: ${quantity}`);
299
+ });
300
+
301
+ window.addEventListener('lce:actions.product_quantity_decrease', (event) => {
302
+ const { identifier, quantity } = event.detail.data;
303
+ console.log(`Quantity decreased to: ${quantity}`);
304
+ });
305
+ ```
306
+
307
+ ## Customization
308
+
309
+ ### Theme Configuration
310
+
311
+ Customize product appearance globally:
312
+
313
+ ```javascript
314
+ const client = await Elements('YOUR_API_KEY', {
315
+ env: 'production',
316
+ customTheme: {
317
+ product: {
318
+ theme: {
319
+ backgroundColor: '#ffffff'
320
+ },
321
+ layout: {
322
+ showImages: true,
323
+ showOnlyMainImage: false, // Show all images or just the main one
324
+ showTitle: true,
325
+ showDescription: true,
326
+ showQuantityCounter: true,
327
+ showOffHours: true, // Show when retailer is closed
328
+ quantityCounterStyle: 'outlined', // or 'ghost'
329
+ fulfillmentDisplay: 'carousel', // or 'popup'
330
+ enableShippingFulfillment: true,
331
+ enableOnDemandFulfillment: true,
332
+ addToCartButtonText: 'Add to Cart',
333
+ addToCartButtonShowTotalPrice: true,
334
+ buyNowButtonText: 'Buy Now',
335
+ preSaleButtonText: 'Pre-Order',
336
+ noAvailabilityText: 'Not available in your area'
337
+ }
338
+ }
339
+ }
340
+ });
341
+ ```
342
+
343
+ ### Global Theme
344
+
345
+ Set colors, fonts, and styles that apply to all components:
346
+
347
+ ```javascript
348
+ customTheme: {
349
+ global: {
350
+ theme: {
351
+ primaryColor: '#007bff',
352
+ accentColor: '#28a745',
353
+ buttonCornerRadius: '8px',
354
+ cardCornerRadius: '12px',
355
+ headingFont: {
356
+ name: 'Poppins',
357
+ weights: [400, 600, 700]
358
+ },
359
+ paragraphFont: {
360
+ name: 'Inter',
361
+ weights: [400, 500]
362
+ }
363
+ }
364
+ }
365
+ }
366
+ ```
367
+
368
+ See [Theming Guide](./theming.md) for complete theming options.
369
+
370
+ ## Address Requirement
371
+
372
+ Products require a delivery address for:
373
+ - Availability checking
374
+ - Accurate pricing
375
+ - Delivery options
376
+
377
+ ### Automatic Address Collection
378
+
379
+ If no address is set, the SDK automatically:
380
+ 1. Prompts for address when user clicks "Add to Cart"
381
+ 2. Shows address input drawer
382
+ 3. Validates and saves address
383
+ 4. Completes the add-to-cart action
384
+
385
+ ### Pre-set Address
386
+
387
+ Set address programmatically to skip prompting:
388
+
389
+ ```javascript
390
+ // Using Google Places ID
391
+ await window.elements.actions.address.setAddressByPlacesId('ChIJ...');
392
+
393
+ // Or manually
394
+ await window.elements.actions.address.setAddressManually(
395
+ {
396
+ one: '123 Main St',
397
+ two: 'Apt 4',
398
+ city: 'New York',
399
+ state: 'NY',
400
+ zip: '10001'
401
+ },
402
+ {
403
+ latitude: 40.7128,
404
+ longitude: -74.0060
405
+ }
406
+ );
407
+ ```
408
+
409
+ ## Presale Products
410
+
411
+ Products in presale mode:
412
+ - Display "Pre-Order" button (customizable text)
413
+ - Cannot be added to cart, it will send the user to checkout directly
414
+ - Show presale countdown if configured
415
+ - Display expected availability date
416
+
417
+ Presale products are handled automatically; no special configuration needed.
418
+
419
+ ## Component Management
420
+
421
+ ### Rerender Product
422
+
423
+ Force a product to reload and rerender:
424
+
425
+ ```javascript
426
+ const components = window.elements.getInjectedComponents();
427
+ const productComponent = components.get('product-1');
428
+
429
+ if (productComponent) {
430
+ productComponent.rerender();
431
+ }
432
+ ```
433
+
434
+ ### Get Component Type
435
+
436
+ Check if a component is a product:
437
+
438
+ ```javascript
439
+ const component = components.get('product-1');
440
+ const type = component.getType();
441
+ console.log(type); // 'product'
442
+ ```
443
+
444
+ ### Get Container Element
445
+
446
+ Access the container DOM element:
447
+
448
+ ```javascript
449
+ const component = components.get('product-1');
450
+ const container = component.getElement();
451
+ console.log(container); // <div id="product-1">...</div>
452
+ ```
453
+
454
+ ## Error Handling
455
+
456
+ ### Product Not Found
457
+
458
+ If a product identifier doesn't exist:
459
+
460
+ ```javascript
461
+ // An error view is shown in the container
462
+ // Console logs: [LiquidCommerce Elements] Product not found: invalid_id
463
+ ```
464
+
465
+ ### No Availability
466
+
467
+ If a product isn't available in the user's location:
468
+
469
+ ```javascript
470
+ // Shows: "Not available in your area" (customizable)
471
+ // User cannot add to cart
472
+ ```
473
+
474
+ ### Loading Errors
475
+
476
+ If product data fails to load:
477
+
478
+ ```javascript
479
+ // Shows error view with retry option
480
+ // Console logs detailed error information
481
+ ```
482
+
483
+ ## Use Cases
484
+
485
+ ### Basic Product Page
486
+
487
+ ```html
488
+ <!DOCTYPE html>
489
+ <html>
490
+ <head>
491
+ <script
492
+ defer
493
+ data-liquid-commerce-elements
494
+ data-token="YOUR_API_KEY"
495
+ data-env="production"
496
+ data-container-1="product"
497
+ data-product-1="00619947000020"
498
+ type="text/javascript"
499
+ src="https://assets-elements.liquidcommerce.us/all/elements.js"
500
+ ></script>
501
+ </head>
502
+ <body>
503
+ <div id="product"></div>
504
+ </body>
505
+ </html>
506
+ ```
507
+
508
+ ### Dynamic Product Selection
509
+
510
+ ```javascript
511
+ import { Elements } from '@liquidcommerce/elements-sdk';
512
+
513
+ const client = await Elements('YOUR_API_KEY', { env: 'production' });
514
+
515
+ // User selects product from dropdown
516
+ document.getElementById('product-selector').addEventListener('change', async (e) => {
517
+ const selectedId = e.target.value;
518
+
519
+ // Clear existing product
520
+ document.getElementById('product').innerHTML = '';
521
+
522
+ // Inject new product
523
+ await client.injectProductElement([
524
+ { containerId: 'product', identifier: selectedId }
525
+ ]);
526
+ });
527
+ ```
528
+
529
+ ### Analytics Integration
530
+
531
+ ```javascript
532
+ // Track product views
533
+ window.addEventListener('lce:actions.product_loaded', (event) => {
534
+ gtag('event', 'view_item', {
535
+ items: [{
536
+ item_id: event.detail.data.identifier,
537
+ item_name: event.detail.data.name,
538
+ price: event.detail.data.price / 100
539
+ }]
540
+ });
541
+ });
542
+
543
+ // Track add to cart
544
+ window.addEventListener('lce:actions.product_add_to_cart', (event) => {
545
+ gtag('event', 'add_to_cart', {
546
+ items: [{
547
+ item_id: event.detail.data.identifier,
548
+ quantity: event.detail.data.quantity
549
+ }]
550
+ });
551
+ });
552
+ ```
553
+
554
+ ### Multi-Product Gallery
555
+
556
+ ```javascript
557
+ const products = [
558
+ '00619947000020',
559
+ '08504405135',
560
+ '08068660001',
561
+ '07549900125'
562
+ ];
563
+
564
+ const client = await Elements('YOUR_API_KEY', { env: 'production' });
565
+
566
+ // Create containers
567
+ const gallery = document.getElementById('product-gallery');
568
+ const productParams = products.map((id, index) => {
569
+ const container = document.createElement('div');
570
+ container.className = 'product-card';
571
+ container.id = `product-${index}`;
572
+ gallery.appendChild(container);
573
+
574
+ return { containerId: `product-${index}`, identifier: id };
575
+ });
576
+
577
+ // Inject all products
578
+ await client.injectProductElement(productParams);
579
+ ```
580
+
581
+ ## Best Practices
582
+
583
+ ### Container Sizing
584
+
585
+ Provide adequate space for the product component:
586
+
587
+ ```css
588
+ #product-display {
589
+ min-height: 600px; /* Prevents layout shift */
590
+ max-width: 1200px;
591
+ margin: 0 auto;
592
+ }
593
+ ```
594
+
595
+ ### Loading States
596
+
597
+ Show a loading indicator while the product loads:
598
+
599
+ ```javascript
600
+ // Show loader
601
+ document.getElementById('product').innerHTML = '<div class="loader">Loading...</div>';
602
+
603
+ await client.injectProductElement([
604
+ { containerId: 'product', identifier: '00619947000020' }
605
+ ]);
606
+
607
+ // Loader is automatically replaced when product loads
608
+ ```
609
+
610
+ ### Error Handling
611
+
612
+ Listen for errors and provide fallback:
613
+
614
+ ```javascript
615
+ try {
616
+ await client.injectProductElement([
617
+ { containerId: 'product', identifier: productId }
618
+ ]);
619
+ } catch (error) {
620
+ console.error('Failed to load product:', error);
621
+ document.getElementById('product').innerHTML =
622
+ '<p>Unable to load product. Please try again later.</p>';
623
+ }
624
+ ```
625
+
626
+ ### Performance
627
+
628
+ For product listings, lazy load products as they come into view:
629
+
630
+ ```javascript
631
+ const observer = new IntersectionObserver(async (entries) => {
632
+ for (const entry of entries) {
633
+ if (entry.isIntersecting) {
634
+ const container = entry.target;
635
+ const productId = container.dataset.productId;
636
+
637
+ await client.injectProductElement([
638
+ { containerId: container.id, identifier: productId }
639
+ ]);
640
+
641
+ observer.unobserve(container);
642
+ }
643
+ }
644
+ });
645
+
646
+ // Observe all product containers
647
+ document.querySelectorAll('.product-placeholder').forEach(el => {
648
+ observer.observe(el);
649
+ });
650
+ ```
651
+
652
+ ## Troubleshooting
653
+
654
+ ### Product Not Displaying
655
+
656
+ 1. Check browser console for errors
657
+ 2. Verify container ID exists in the DOM
658
+ 3. Confirm product identifier is valid
659
+ 4. Check that SDK is initialized (`window.elements` exists)
660
+
661
+ ### Wrong Pricing
662
+
663
+ - Ensure user's address is set correctly
664
+ - Verify product has availability in user's location
665
+ - Check that fulfillment type is supported
666
+
667
+ ### Images Not Loading
668
+
669
+ - Check network tab for 404 errors
670
+ - Verify product has images in the catalog
671
+ - Ensure no ad blockers are interfering
672
+
673
+ ### Size Selector Not Showing
674
+
675
+ - Product must have multiple sizes to show selector
676
+ - Check that sizes have availability
677
+ - Verify theme config hasn't hidden the selector
678
+
679
+ ## See Also
680
+
681
+ - [Cart Component](./cart-component.md) - Shopping cart functionality
682
+ - [Address Component](./address-component.md) - Location management
683
+ - [Checkout Component](./checkout-component.md) - Complete purchase flow
684
+ - [Theming](./theming.md) - Customize appearance
685
+ - [Events](./events.md) - All available events
686
+ - [Actions API](../api/actions/product-actions.md) - Product actions reference