@liquidcommerce/elements-sdk 2.7.8 → 2.7.10

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 (36) hide show
  1. package/README.md +1 -1
  2. package/dist/index.checkout.esm.js +6884 -6883
  3. package/dist/index.esm.js +11255 -11181
  4. package/dist/types/index.checkout.d.ts +1 -1
  5. package/dist/types/index.checkout.umd.d.ts +1 -1
  6. package/dist/types/interfaces/api/cart.interface.d.ts +1 -0
  7. package/dist/types/interfaces/api/checkout.interface.d.ts +1 -0
  8. package/dist/types/interfaces/api/product.interface.d.ts +1 -0
  9. package/dist/types/modules/product/components/product-add-to-cart-section.component.d.ts +1 -0
  10. package/dist/types/modules/product-list/components/product-list-product-pre-cart.component.d.ts +3 -0
  11. package/dist/types/modules/product-list/product-list.component.d.ts +0 -1
  12. package/dist/types/modules/ui-components/engraving/engraving-view.component.d.ts +1 -0
  13. package/docs/v1/README.md +1 -1
  14. package/docs/v1/api/actions/checkout-actions.md +40 -0
  15. package/docs/v1/api/actions/product-actions.md +44 -1
  16. package/docs/v1/api/client.md +13 -0
  17. package/docs/v1/api/configuration.md +528 -1
  18. package/docs/v1/api/injection-methods.md +18 -4
  19. package/docs/v1/api/typescript-types.md +398 -1
  20. package/docs/v1/examples/advanced-patterns.md +102 -0
  21. package/docs/v1/examples/checkout-flow.md +2 -2
  22. package/docs/v1/examples/multi-product-page.md +2 -2
  23. package/docs/v1/examples/simple-product-page.md +1 -1
  24. package/docs/v1/getting-started/concepts.md +3 -3
  25. package/docs/v1/getting-started/installation.md +3 -3
  26. package/docs/v1/getting-started/quick-start.md +6 -6
  27. package/docs/v1/guides/address-component.md +1 -1
  28. package/docs/v1/guides/best-practices.md +29 -0
  29. package/docs/v1/guides/cart-component.md +7 -7
  30. package/docs/v1/guides/checkout-component.md +2 -2
  31. package/docs/v1/guides/product-component.md +5 -5
  32. package/docs/v1/guides/product-list-component.md +72 -163
  33. package/docs/v1/integration/laravel.md +1 -1
  34. package/docs/v1/integration/vanilla-js.md +2 -2
  35. package/docs/v1/reference/troubleshooting.md +64 -0
  36. package/package.json +14 -21
@@ -188,6 +188,35 @@ document.getElementById('clear-cart-btn').addEventListener('click', async () =>
188
188
  });
189
189
  ```
190
190
 
191
+ ## Cleanup
192
+
193
+ ### Destroy Components When Done
194
+
195
+ In single-page applications, clean up when navigating away:
196
+
197
+ ```javascript
198
+ // Destroy individual component
199
+ const component = client.getInjectedComponents().get('product-1');
200
+ component?.destroy();
201
+
202
+ // Destroy entire client (on route change)
203
+ client.destroy();
204
+ ```
205
+
206
+ ### Avoid Leaking References
207
+
208
+ ```javascript
209
+ // Good - clean up on unmount
210
+ useEffect(() => {
211
+ const init = async () => {
212
+ const c = await Elements('KEY', { env: 'production' });
213
+ clientRef.current = c;
214
+ };
215
+ init();
216
+ return () => clientRef.current?.destroy();
217
+ }, []);
218
+ ```
219
+
191
220
  ## Events
192
221
 
193
222
  ### Use Events for Reactive Updates
@@ -31,7 +31,7 @@ Add a cart button using data attributes on the SDK script:
31
31
  data-env="production"
32
32
  data-cart-button="header-cart"
33
33
  type="text/javascript"
34
- src="https://assets-elements.liquidcommerce.us/all/elements.js"
34
+ src="https://elements.reservebar-worker.workers.dev/all/elements.js"
35
35
  ></script>
36
36
 
37
37
  <div id="header-cart"></div>
@@ -49,7 +49,7 @@ Show the number of items in the cart:
49
49
  data-env="production"
50
50
  data-cart-badge-button="header-cart"
51
51
  type="text/javascript"
52
- src="https://assets-elements.liquidcommerce.us/all/elements.js"
52
+ src="https://elements.reservebar-worker.workers.dev/all/elements.js"
53
53
  ></script>
54
54
 
55
55
  <div id="header-cart"></div>
@@ -67,7 +67,7 @@ Create a floating button (bottom-right corner):
67
67
  data-env="production"
68
68
  data-cart-button
69
69
  type="text/javascript"
70
- src="https://assets-elements.liquidcommerce.us/all/elements.js"
70
+ src="https://elements.reservebar-worker.workers.dev/all/elements.js"
71
71
  ></script>
72
72
  ```
73
73
 
@@ -103,7 +103,7 @@ If you want to control cart opening manually:
103
103
  data-env="production"
104
104
  data-cart-button-hidden
105
105
  type="text/javascript"
106
- src="https://assets-elements.liquidcommerce.us/all/elements.js"
106
+ src="https://elements.reservebar-worker.workers.dev/all/elements.js"
107
107
  ></script>
108
108
  ```
109
109
 
@@ -514,7 +514,7 @@ Or with data attributes:
514
514
  data-promo-active-from="2024-06-01T00:00:00Z"
515
515
  data-promo-active-until="2024-08-31T23:59:59Z"
516
516
  type="text/javascript"
517
- src="https://assets-elements.liquidcommerce.us/all/elements.js"
517
+ src="https://elements.reservebar-worker.workers.dev/all/elements.js"
518
518
  ></script>
519
519
  ```
520
520
 
@@ -530,7 +530,7 @@ Apply promo codes automatically via URL parameters:
530
530
  data-env="production"
531
531
  data-promo-code-param="lce_promo"
532
532
  type="text/javascript"
533
- src="https://assets-elements.liquidcommerce.us/all/elements.js"
533
+ src="https://elements.reservebar-worker.workers.dev/all/elements.js"
534
534
  ></script>
535
535
  ```
536
536
 
@@ -593,7 +593,7 @@ Or with data attributes:
593
593
  data-env="production"
594
594
  data-checkout-url="https://yoursite.com/checkout/{token}"
595
595
  type="text/javascript"
596
- src="https://assets-elements.liquidcommerce.us/all/elements.js"
596
+ src="https://elements.reservebar-worker.workers.dev/all/elements.js"
597
597
  ></script>
598
598
  ```
599
599
 
@@ -48,7 +48,7 @@ Configure checkout to load from URL parameter:
48
48
  data-checkout-container="checkout"
49
49
  data-checkout-param="lce_checkout"
50
50
  type="text/javascript"
51
- src="https://assets-elements.liquidcommerce.us/all/elements.js"
51
+ src="https://elements.reservebar-worker.workers.dev/all/elements.js"
52
52
  ></script>
53
53
 
54
54
  <div id="checkout"></div>
@@ -79,7 +79,7 @@ Or with data attributes:
79
79
  data-env="production"
80
80
  data-checkout-url="https://yoursite.com/checkout?lce_checkout={token}"
81
81
  type="text/javascript"
82
- src="https://assets-elements.liquidcommerce.us/all/elements.js"
82
+ src="https://elements.reservebar-worker.workers.dev/all/elements.js"
83
83
  ></script>
84
84
  ```
85
85
 
@@ -29,7 +29,7 @@ The simplest way to add a product is using HTML data attributes:
29
29
  data-container-1="product-display"
30
30
  data-product-1="00619947000020"
31
31
  type="text/javascript"
32
- src="https://assets-elements.liquidcommerce.us/all/elements.js"
32
+ src="https://elements.reservebar-worker.workers.dev/all/elements.js"
33
33
  ></script>
34
34
 
35
35
  <div id="product-display"></div>
@@ -48,7 +48,7 @@ The simplest way to add a product is using HTML data attributes:
48
48
  data-container-2="product-2"
49
49
  data-product-2="08504405135"
50
50
  type="text/javascript"
51
- src="https://assets-elements.liquidcommerce.us/all/elements.js"
51
+ src="https://elements.reservebar-worker.workers.dev/all/elements.js"
52
52
  ></script>
53
53
 
54
54
  <div id="product-1"></div>
@@ -66,7 +66,7 @@ Use `data-lce-product` on any div:
66
66
  data-token="YOUR_API_KEY"
67
67
  data-env="production"
68
68
  type="text/javascript"
69
- src="https://assets-elements.liquidcommerce.us/all/elements.js"
69
+ src="https://elements.reservebar-worker.workers.dev/all/elements.js"
70
70
  ></script>
71
71
 
72
72
  <div data-lce-product="00619947000020"></div>
@@ -86,7 +86,7 @@ For many products, use a JSON script tag:
86
86
  data-token="YOUR_API_KEY"
87
87
  data-env="production"
88
88
  type="text/javascript"
89
- src="https://assets-elements.liquidcommerce.us/all/elements.js"
89
+ src="https://elements.reservebar-worker.workers.dev/all/elements.js"
90
90
  ></script>
91
91
 
92
92
  <script data-liquid-commerce-elements-products type="application/json">
@@ -496,7 +496,7 @@ If product data fails to load:
496
496
  data-container-1="product"
497
497
  data-product-1="00619947000020"
498
498
  type="text/javascript"
499
- src="https://assets-elements.liquidcommerce.us/all/elements.js"
499
+ src="https://elements.reservebar-worker.workers.dev/all/elements.js"
500
500
  ></script>
501
501
  </head>
502
502
  <body>
@@ -25,14 +25,14 @@ Use data attributes to configure the product list:
25
25
  data-liquid-commerce-elements
26
26
  data-token="YOUR_API_KEY"
27
27
  data-env="production"
28
- src="https://assets-elements.liquidcommerce.us/all/elements.js"
28
+ src="https://elements.reservebar-worker.workers.dev/all/elements.js"
29
29
  ></script>
30
30
 
31
31
  <div
32
32
  data-liquid-commerce-elements-products-list="my-collection-slug"
33
33
  data-rows="3"
34
34
  data-columns="4"
35
- data-filters="price,brand,category"
35
+ data-filters="price,brands,categories"
36
36
  data-product-url="/product/{identifier}"
37
37
  ></div>
38
38
  ```
@@ -55,7 +55,7 @@ Separate containers for search and filters:
55
55
  <div data-liquid-commerce-elements-products-list-search="my-collection-slug"></div>
56
56
 
57
57
  <!-- Filters container -->
58
- <div data-liquid-commerce-elements-products-list-filters="my-collection-slug" data-filters="price,brand,fulfillment"></div>
58
+ <div data-liquid-commerce-elements-products-list-filters="my-collection-slug" data-filters="price,brands,fulfillment"></div>
59
59
 
60
60
  <!-- Product list -->
61
61
  <div
@@ -76,117 +76,52 @@ const client = await Elements('YOUR_API_KEY', { env: 'production' });
76
76
  // Inject product list
77
77
  await client.injectProductList({
78
78
  containerId: 'products',
79
+ slug: 'my-collection-slug',
79
80
  rows: 3,
80
81
  columns: 4,
81
- filters: ['price', 'brand', 'category', 'fulfillment'],
82
+ filters: ['price', 'brands', 'categories', 'fulfillment'],
82
83
  productUrl: '/product/{identifier}'
83
84
  });
84
85
 
85
86
  // Inject search (optional)
86
87
  await client.injectProductListSearch({
87
- containerId: 'search'
88
+ containerId: 'search',
89
+ slug: 'my-collection-slug'
88
90
  });
89
91
 
90
92
  // Inject filters (optional)
91
93
  await client.injectProductListFilters({
92
94
  containerId: 'filters',
93
- filters: ['price', 'brand']
95
+ slug: 'my-collection-slug',
96
+ filters: ['price', 'brands']
94
97
  });
95
98
  ```
96
99
 
97
100
  ## Available Filters
98
101
 
99
- ### Price Range
100
-
101
- Filter products by price:
102
+ The following filter type values can be used in the `filters` array:
103
+
104
+ | Filter Value | Description |
105
+ |----------------|------------------------------------------|
106
+ | `'price'` | Price range slider with min/max values |
107
+ | `'brands'` | Checkboxes for available brands |
108
+ | `'categories'` | Category selection checkboxes |
109
+ | `'fulfillment'`| Shipping vs. on-demand delivery toggle |
110
+ | `'engraving'` | Filter by personalization support |
111
+ | `'sizes'` | Filter by product size/volume |
112
+ | `'flavor'` | Filter by flavor profile |
113
+ | `'region'` | Filter by region of origin |
114
+ | `'variety'` | Filter by product variety |
115
+ | `'vintage'` | Filter by vintage year |
116
+ | `'country'` | Filter by country of origin |
117
+ | `'appellation'`| Filter by appellation |
118
+ | `'materials'` | Filter by materials |
102
119
 
103
120
  ```javascript
104
- filters: ['price']
121
+ // Example: use multiple filters
122
+ filters: ['price', 'brands', 'categories', 'fulfillment', 'sizes']
105
123
  ```
106
124
 
107
- Shows price range slider with min/max values.
108
-
109
- ### Brand
110
-
111
- Filter by brand/manufacturer:
112
-
113
- ```javascript
114
- filters: ['brand']
115
- ```
116
-
117
- Shows checkboxes for all available brands.
118
-
119
- ### Category
120
-
121
- Filter by product category:
122
-
123
- ```javascript
124
- filters: ['category']
125
- ```
126
-
127
- Shows category selection dropdown or checkboxes.
128
-
129
- ### Fulfillment Type
130
-
131
- Filter by shipping vs. on-demand delivery:
132
-
133
- ```javascript
134
- filters: ['fulfillment']
135
- ```
136
-
137
- Options:
138
- - Shipping only
139
- - On-demand delivery only
140
- - Both
141
-
142
- ### Engraving
143
-
144
- Filter products that support personalization:
145
-
146
- ```javascript
147
- filters: ['engraving']
148
- ```
149
-
150
- Options:
151
- - Can be engraved
152
- - Cannot be engraved
153
- - Show all
154
-
155
- ### Size
156
-
157
- Filter by product size (volume):
158
-
159
- ```javascript
160
- filters: ['size']
161
- ```
162
-
163
- Shows available sizes (e.g., 750ml, 1L, 1.75L).
164
-
165
- ### Rating
166
-
167
- Filter by customer ratings:
168
-
169
- ```javascript
170
- filters: ['rating']
171
- ```
172
-
173
- Options:
174
- - 4+ stars
175
- - 3+ stars
176
- - All ratings
177
-
178
- ### Availability
179
-
180
- Filter by stock status:
181
-
182
- ```javascript
183
- filters: ['availability']
184
- ```
185
-
186
- Options:
187
- - In stock only
188
- - All products
189
-
190
125
  ## Search Functionality
191
126
 
192
127
  ### Search Box
@@ -208,15 +143,7 @@ The search component provides full-text search across:
208
143
 
209
144
  ### Programmatic Search
210
145
 
211
- Control search via JavaScript:
212
-
213
- ```javascript
214
- // Get current search term
215
- const currentSearch = store.get('productList.search.query');
216
-
217
- // Set search programmatically
218
- store.set('productList.search.query', 'whiskey');
219
- ```
146
+ Search is controlled through the injected search component. Use `injectProductListSearch()` to add a search box that automatically filters the associated product list.
220
147
 
221
148
  ## Grid Layout
222
149
 
@@ -238,6 +165,7 @@ The grid automatically adjusts for screen sizes:
238
165
  ```javascript
239
166
  await client.injectProductList({
240
167
  containerId: 'products',
168
+ slug: 'my-collection',
241
169
  rows: 5, // Number of rows per page
242
170
  columns: 4 // Columns in grid (desktop)
243
171
  });
@@ -295,33 +223,46 @@ Each product card shows:
295
223
 
296
224
  ### Theme Configuration
297
225
 
226
+ Product list theming is applied per-list using the collection slug as a key:
227
+
298
228
  ```javascript
299
229
  const client = await Elements('YOUR_API_KEY', {
300
230
  env: 'production',
301
231
  customTheme: {
302
232
  productList: {
303
233
  theme: {
304
- cardBackgroundColor: '#ffffff',
305
- cardBorderColor: '#e0e0e0',
306
- cardHoverShadow: '0 4px 8px rgba(0,0,0,0.1)'
234
+ backgroundColor: '#ffffff'
307
235
  },
308
236
  layout: {
309
- showProductImages: true,
310
- showBrand: true,
311
- showPrice: true,
312
- showRating: true,
313
- showAvailability: true,
314
- showAddToCartButton: false, // Enable quick add
315
- cardImageAspectRatio: '1:1', // or '4:3', '16:9'
316
- gridGap: '20px',
317
- enableInfiniteScroll: true,
318
- productsPerPage: 12 // Override rows × columns
237
+ lists: {
238
+ 'my-collection-slug': {
239
+ productCard: {
240
+ style: 'card', // 'card' or 'ghost'
241
+ cornerRadius: '8px',
242
+ showPrice: true,
243
+ showSizes: true,
244
+ showRetailerName: true,
245
+ showFulfillmentOptions: true,
246
+ enableShippingFulfillment: true,
247
+ enableOnDemandFulfillment: true,
248
+ enablePersonalization: true,
249
+ showQuantityCounter: true,
250
+ enablePreCart: true,
251
+ showCollectionTags: false
252
+ },
253
+ presentationMode: 'drawer', // 'drawer' or 'modal'
254
+ rows: 4,
255
+ columns: 3
256
+ }
257
+ }
319
258
  }
320
259
  }
321
260
  }
322
261
  });
323
262
  ```
324
263
 
264
+ See [Configuration Reference](../api/configuration.md#product-list-theme) for the complete list of options.
265
+
325
266
  ## Use Cases
326
267
 
327
268
  ### Category Page
@@ -336,7 +277,7 @@ const client = await Elements('YOUR_API_KEY', {
336
277
  data-liquid-commerce-elements
337
278
  data-token="YOUR_API_KEY"
338
279
  data-env="production"
339
- src="https://assets-elements.liquidcommerce.us/all/elements.js"
280
+ src="https://elements.reservebar-worker.workers.dev/all/elements.js"
340
281
  ></script>
341
282
  </head>
342
283
  <body>
@@ -348,7 +289,7 @@ const client = await Elements('YOUR_API_KEY', {
348
289
  <div class="catalog">
349
290
  <!-- Filters sidebar -->
350
291
  <aside>
351
- <div data-liquid-commerce-elements-products-list-filters="whiskey-collection" data-filters="price,brand,size"></div>
292
+ <div data-liquid-commerce-elements-products-list-filters="whiskey-collection" data-filters="price,brands,sizes"></div>
352
293
  </aside>
353
294
 
354
295
  <!-- Product grid -->
@@ -372,51 +313,18 @@ import { Elements } from '@liquidcommerce/elements-sdk';
372
313
 
373
314
  const client = await Elements('YOUR_API_KEY', { env: 'production' });
374
315
 
375
- // Get search query from URL
376
- const params = new URLSearchParams(window.location.search);
377
- const searchQuery = params.get('q');
378
-
379
- // Inject product list
316
+ // Inject product list with search
380
317
  await client.injectProductList({
381
318
  containerId: 'search-results',
319
+ slug: 'all-products',
382
320
  rows: 5,
383
321
  columns: 4,
384
- filters: ['price', 'brand', 'category']
385
- });
386
-
387
- // Apply search
388
- if (searchQuery) {
389
- store.set('productList.search.query', searchQuery);
390
- }
391
- ```
392
-
393
- ### Dynamic Filtering
394
-
395
- ```javascript
396
- // Listen for filter changes
397
- window.addEventListener('lce:productList.filterChanged', (event) => {
398
- const { filterType, filterValue } = event.detail;
399
-
400
- console.log(`Filter changed: ${filterType} = ${filterValue}`);
401
-
402
- // Track filter usage
403
- gtag('event', 'filter_applied', {
404
- filter_type: filterType,
405
- filter_value: filterValue
406
- });
322
+ filters: ['price', 'brands', 'categories']
407
323
  });
408
324
 
409
- // Listen for search
410
- window.addEventListener('lce:productList.searchChanged', (event) => {
411
- const { query, resultCount } = event.detail;
412
-
413
- console.log(`Search: "${query}" (${resultCount} results)`);
414
-
415
- // Track search
416
- gtag('event', 'search', {
417
- search_term: query,
418
- result_count: resultCount
419
- });
325
+ await client.injectProductListSearch({
326
+ containerId: 'search-box',
327
+ slug: 'all-products'
420
328
  });
421
329
  ```
422
330
 
@@ -442,6 +350,7 @@ switch (productType) {
442
350
 
443
351
  await client.injectProductList({
444
352
  containerId: 'products',
353
+ slug: 'all-products',
445
354
  rows: 3,
446
355
  columns: 4,
447
356
  productUrl: urlPattern
@@ -493,9 +402,10 @@ For catalogs with thousands of products:
493
402
  ```javascript
494
403
  await client.injectProductList({
495
404
  containerId: 'products',
405
+ slug: 'all-products',
496
406
  rows: 3,
497
407
  columns: 4,
498
- filters: ['price', 'brand'], // Limit filters to most useful
408
+ filters: ['price', 'brands'], // Limit filters to most useful
499
409
  // More rows = larger pages = fewer API calls
500
410
  });
501
411
  ```
@@ -514,13 +424,11 @@ await client.injectProductList({
514
424
 
515
425
  ### Show Result Counts
516
426
 
517
- Display the number of products found:
427
+ The product list component automatically displays the number of results. You can also track cart additions from the list using standard cart events:
518
428
 
519
429
  ```javascript
520
- window.addEventListener('lce:productList.updated', (event) => {
521
- const { totalProducts, displayedProducts } = event.detail;
522
- document.getElementById('result-count').textContent =
523
- `Showing ${displayedProducts} of ${totalProducts} products`;
430
+ window.addEventListener('lce:actions.cart_item_added', (event) => {
431
+ console.log('Item added from product list:', event.detail.data);
524
432
  });
525
433
  ```
526
434
 
@@ -553,9 +461,10 @@ For category pages, pre-select relevant filters:
553
461
  // On whiskey category page
554
462
  await client.injectProductList({
555
463
  containerId: 'products',
464
+ slug: 'whiskey-collection',
556
465
  rows: 4,
557
466
  columns: 3,
558
- filters: ['price', 'brand', 'size'] // Most relevant for whiskey
467
+ filters: ['price', 'brands', 'sizes'] // Most relevant for whiskey
559
468
  });
560
469
  ```
561
470
 
@@ -21,7 +21,7 @@ Use the CDN build in a Blade view for a quick server-rendered setup.
21
21
  data-container-1="product"
22
22
  data-product-1="00619947000020"
23
23
  type="text/javascript"
24
- src="https://assets-elements.liquidcommerce.us/all/elements.js"
24
+ src="https://elements.reservebar-worker.workers.dev/all/elements.js"
25
25
  ></script>
26
26
  </head>
27
27
  <body>
@@ -21,7 +21,7 @@ Use the CDN build for the fastest setup, or use NPM if you have a build step.
21
21
  data-product-1="00619947000020"
22
22
  data-cart-badge-button="header-cart"
23
23
  type="text/javascript"
24
- src="https://assets-elements.liquidcommerce.us/all/elements.js"
24
+ src="https://elements.reservebar-worker.workers.dev/all/elements.js"
25
25
  ></script>
26
26
  </head>
27
27
  <body>
@@ -46,7 +46,7 @@ Use the CDN build for the fastest setup, or use NPM if you have a build step.
46
46
  data-token="YOUR_API_KEY"
47
47
  data-env="production"
48
48
  type="text/javascript"
49
- src="https://assets-elements.liquidcommerce.us/all/elements.js"
49
+ src="https://elements.reservebar-worker.workers.dev/all/elements.js"
50
50
  ></script>
51
51
 
52
52
  <script>
@@ -64,6 +64,70 @@ This is expected behavior. The SDK ships an SSR stub that is automatically resol
64
64
 
65
65
  **No action required** — initialize the SDK in a client-only lifecycle hook (`useEffect`, `onMounted`, etc.) and the real client will activate in the browser.
66
66
 
67
+ ## Styling Not Applied / CSS Not Working
68
+
69
+ **Symptoms:** Components render but custom styles are ignored, or host page styles leak into components.
70
+
71
+ **Cause:** Components use Shadow DOM for style isolation.
72
+
73
+ **Fixes:**
74
+ - Use `customTheme` in the client configuration to style components -- external CSS cannot penetrate Shadow DOM.
75
+ - For debugging, enable `development.openShadowDom: true` to disable Shadow DOM temporarily.
76
+ - Use the debug panel (`debugMode: 'panel'`) to inspect component state.
77
+
78
+ ## Component Not Updating After Data Changes
79
+
80
+ **Symptoms:** Component shows stale data after an action completes.
81
+
82
+ **Fixes:**
83
+ - Call `.rerender()` on the injected component to force a refresh:
84
+ ```javascript
85
+ const components = client.getInjectedComponents();
86
+ components.get('product-1')?.rerender();
87
+ ```
88
+ - Ensure you're not holding a stale reference -- call `getInjectedComponents()` again after injection.
89
+
90
+ ## Cleanup / Memory Leaks
91
+
92
+ **Symptoms:** Components persist after navigating away in SPA, or duplicate components appear.
93
+
94
+ **Fixes:**
95
+ - Call `destroy()` on individual injected components when removing them:
96
+ ```javascript
97
+ const component = client.getInjectedComponents().get('product-1');
98
+ component?.destroy();
99
+ ```
100
+ - Call `client.destroy()` when tearing down the entire SDK (e.g., on SPA route change).
101
+
102
+ ## Promo Ticker Not Showing
103
+
104
+ **Symptoms:** Promo ticker is configured but not visible.
105
+
106
+ **Fixes:**
107
+ - Verify all 5 required fields are set: `promoCode`, `text`, `separator`, `activeFrom`, `activeUntil`.
108
+ - Check that the current time falls between `activeFrom` and `activeUntil` (ISO 8601 UTC format).
109
+ - Ensure `global.layout.allowPromoCodes` is not set to `false`.
110
+
111
+ ## Ad Blockers Blocking SDK Requests
112
+
113
+ **Symptoms:** Network requests fail, products don't load, but no code errors.
114
+
115
+ **Fixes:**
116
+ - Set up a proxy to route API requests through your own domain:
117
+ ```javascript
118
+ proxy: { baseUrl: 'https://yoursite.com/api/elements-proxy' }
119
+ ```
120
+ - See [Proxy Setup Guide](../integration/proxy-setup.md) for server-side implementation.
121
+
122
+ ## Performance Issues on Large Product Lists
123
+
124
+ **Symptoms:** Slow rendering or high memory usage with many products.
125
+
126
+ **Fixes:**
127
+ - Reduce `rows` and `columns` to load fewer products per page.
128
+ - Limit the number of active filters.
129
+ - Use the checkout-only build on checkout pages to reduce bundle size.
130
+
67
131
  ## Related Docs
68
132
 
69
133
  - [Installation](../getting-started/installation.md)