---
title: "Liquid debugging and performance guide"
source: https://support.storeconnect.com/articles/liquid-debugging-and-performance
type: article
format: markdown
site: StoreConnect Support — product and developer documentation for StoreConnect
site_index: https://storeconnect.com/llms.txt
docs_index: https://support.storeconnect.com/llms.txt
note: Append .md to any page or article URL on this site to get its Markdown form.
---
# Liquid debugging and performance guide

Learn to debug Liquid templates effectively and measure performance to identify and fix slow sections. This guide covers safe logging practices, performance profiling, and optimization patterns.

## Debugging workflow

A structured approach to debugging:

1. **Identify the problem** — slow render, unexpected output, blank variables
2. **Log relevant variables** — use `{% debug %}` to inspect values safely
3. **Check the web console** — the [web console](web-console) shows debug output (never the page, and never the browser's console)
4. **Profile hotspots** — use `{% timer %}` to measure execution time
5. **Locate bottlenecks** — identify which sections are slow
6. **Optimize or cache** — fix the slow section or wrap in `{% cache %}`
7. **Remove debug tags** — strip all debug/timer before shipping to production

## Safe logging with debug tag

The `{% debug %}` tag writes to the [web console](web-console) only, StoreConnect's own developer console rather than the browser's. It never appears in page output and never reaches the visitor's browser. It also records nothing unless the console was already connected when the page loaded, which is one common reason a `debug` tag appears to do nothing. Two others silence it just as quietly, a controller block that never ran and a cached fragment that hit. The [debug tag reference](debug-tag-reference) covers how to tell all three apart.

### Safe logging patterns


```liquid

{% debug product_id: product.id, price: product.pricing.price %}
```


This outputs to console but not the page.

### What NOT to log

Never log full objects or sensitive data:


```liquid

{%- comment -%} ✗ DO NOT DO THIS {%- endcomment -%}
{% debug customer: current_customer %}
{% debug cart: current_cart %}
{% debug params: current_request.params %}
```


These expose:
- Customer IDs and personal data
- Cart contents and pricing
- All request parameters (including secrets)

### Safe alternatives

Log only the fields you need:


```liquid

{%- comment -%} ✓ CORRECT {%- endcomment -%}
{% debug customer_id: current_customer.id, country: current_customer.billing_address.country %}
{% debug cart_total: current_cart.totals.total, item_count: current_cart.items.size %}
```


### Production debugging

Debug tags in production are silent no-ops if no console is open. However, **always remove them before shipping**:


```bash

grep -r "{% debug" theme/ --exclude-dir=node_modules
```


## Performance profiling with timer

Use `{% timer %}` to measure how long a block takes to render.

### Simple timer


```liquid

{% timer "product_render" %}
  {% render "products/card", product: product %}
{% endtimer %}
```


Output in console: `product_render: 45.23ms`

### Comparing before/after

Measure the same section twice to see improvement:


```liquid

{% timer "before_optimization" %}
  {% for product in all_products limit: 100 %}
    {{ product.name }}
  {% endfor %}
{% endtimer %}

{%- comment -%} after optimization {%- endcomment -%}
{% timer "after_optimization" %}
  {% for product in all_products limit: 100 %}
    {{ product.name | upcase }}
  {% endfor %}
{% endtimer %}
```


### Nested timers

Profile multiple levels:


```liquid

{% timer "render_page" %}
  {% timer "query" %}
    {% query 'Product2' as products, s_c__store__c: current_store.sfid limit: 50 %}
  {% endtimer %}
  
  {% timer "loop_and_render" %}
    {% for product in products %}
      {% render "products/card", product: product %}
    {% endfor %}
  {% endtimer %}
{% endtimer %}
```


Console output:
```
query: 125.45ms
loop_and_render: 89.34ms
render_page: 220.12ms
```

### Interpreting results

- **Query time > 100ms** — consider a more efficient query or add a filter
- **Render loop > 200ms** — consider caching the loop output
- **Each render > 20ms** — snippet is expensive; consider caching individual items

## Cache strategy patterns

Performance improvements usually come from caching, not code optimization.

### Pattern 1: Cache expensive queries


```liquid

{% cache "products_featured", items: [current_store], expires_in: 300 %}
  {% query 'Product2' as products,
      s_c__store__c: current_store.sfid,
      data.featured__c: true,
      isactive: true %}
  
  {%- for product in products -%}
    {{ product.name }}: {{ product.pricing.price | money }}
  {%- endfor -%}
{% endcache %}
```


Cache time: 5 minutes. Re-run query every 5 minutes only.

### Pattern 2: Cache per-product


```liquid

{% for product in all_products limit: 20 %}
  {% cache "product_card", items: [product, current_store, current_customer], expires_in: 600 %}
    {% render "products/card", product: product %}
  {% endcache %}
{% endfor %}
```


Each product gets its own cached copy, valid for 10 minutes.

### Pattern 3: Cache navigation


```liquid

{% cache "header_nav", items: [current_store], expires_in: 3600 %}
  {% render "shared/navigation" %}
{% endcache %}
```


Navigation cache: 1 hour. Expires when Store's Cache Version changes.

### Pattern 4: Cache with customer personalization


```liquid

{% cache "sidebar", items: [current_store, current_customer], expires_in: 600 %}
  {% render "shared/sidebar" %}
{% endcache %}
```


Each customer gets their own sidebar cache. Refreshes every 10 minutes or when cart changes.

### Pattern 5: Skip cache for sensitive content

Never cache:
- Checkout state
- Payment forms
- Consent controls
- Account information
- Promotional codes (cart-dependent)

### Pattern 6: Cache small pieces


```liquid

{%- comment -%} Good: cache the card, not the whole loop {%- endcomment -%}
{% for product in products %}
  {% cache "product_card_{{ product.id }}", items: [product], expires_in: 300 %}
    {% render "products/card", product: product %}
  {% endcache %}
{% endfor %}
```


Small caches are more efficient than one large cache.

## Common performance bottlenecks

### 1. Unscoped queries

❌ **Slow:**

```liquid

{% query 'Product2' as all_products %}
```


This fetches ALL products. If you have 100k products, this is painfully slow.

✅ **Fast:**

```liquid

{% query 'Product2' as products,
    s_c__store__c: current_store.sfid,
    isactive: true,
    data.featured__c: true %}
```


Scope by store, active status, and filters.

### 2. N+1 queries in loops

❌ **Slow:**

```liquid

{% for category in all_categories %}
  {% query 'Product2' as products, s_c__category__c: category.id %}
  {{ products.size }} products
{% endfor %}
```


This runs one query per category (N+1 problem).

✅ **Fast:**

```liquid

{% query 'Product2' as products, s_c__store__c: current_store.sfid %}

{% for category in all_categories %}
  {%- assign category_products = products | where: "s_c__category__c", category.id -%}
  {{ category_products.size }} products
{% endfor %}
```


Query once, filter in memory.

### 3. Uncached expensive snippets in loops

❌ **Slow:**

```liquid

{% for product in products %}
  {% render "expensive_snippet", product: product %}
{% endfor %}
```


Slow snippet rendered 100 times per page view.

✅ **Fast:**

```liquid

{% for product in products %}
  {% cache "snippet_{{ product.id }}", items: [product], expires_in: 600 %}
    {% render "expensive_snippet", product: product %}
  {% endcache %}
{% endfor %}
```


Each product's snippet is cached.

### 4. Large array operations

❌ **Slow:**

```liquid

{% assign all_options = "" | split: "" %}
{% for product in all_products limit: 1000 %}
  {% for variant in product.variants %}
    {% assign all_options = all_options | push: variant.option_values %}
  {% endfor %}
{% endfor %}
```


Array operations are expensive at scale.

✅ **Fast:**

```liquid

{% assign options = all_products | map: "variants" | map: "option_values" | flatten | uniq %}
```


Use filters instead of loops.

## Production considerations

### Removing debug before deploy


```bash

# Find all debug tags
grep -r "{% debug" docs/

# Find all timer tags
grep -r "{% timer" docs/

# Remove them with sed (backup first!)
sed -i.backup 's/{% debug.*%}//g' file.liquid
```


### Monitoring performance

On production, monitor:
- Page render times (goal: < 1s)
- Query execution (goal: < 100ms each)
- Cache hit rate (goal: > 80% for frequently-used caches)

### Performance budgets

Set budgets for each page:
- **Homepage** — < 1s render time
- **Product page** — < 500ms render time
- **Search results** — < 800ms render time

If a page exceeds its budget, profile with timers and cache the hotspot.

---

## Follow StoreConnect

- [Email Newsletter](https://storeconnect.com/c/lp-newsletter)
- [LinkedIn Newsletter](https://www.linkedin.com/build-relation/newsletter-follow?entityUrn=7444956928444862464)
- [YouTube](https://www.youtube.com/channel/UCngKdP2x8l1wcbAKW3tvU8g)
- [LinkedIn](https://www.linkedin.com/company/storeconnect)
- [X / Twitter](https://x.com/storeconnecthq)

## Popular Links

- [Partners](https://storeconnect.com/partners)
- [Become a Partner](https://storeconnect.com/become-a-partner)
- [News](https://storeconnect.com/articles/news)
- [Events](https://storeconnect.com/articles/events)
- [Live Events](https://storeconnect.com/live-events)
- [Feature Comparison](https://storeconnect.com/how-we-compare)
- [Download a free trial](https://appexchange.salesforce.com/appxListingDetail?listingId=a0N3A00000FMkeKUAT)
- [Book a Demo](https://storeconnect.com/contact)

## Documentation

- [Help documentation](https://support.storeconnect.com/help-documentation)
- [AI agents](https://support.storeconnect.com/ai)
- [Videos & tutorials](https://support.storeconnect.com/videos-tutorials)
- [Developer reference](https://support.storeconnect.com/developer-reference)
- [Release notes](https://support.storeconnect.com/release-notes)
- [Troubleshooting](https://support.storeconnect.com/troubleshooting)
- [Trust Center](https://trust.getstoreconnect.com/)
- [Status Page](https://status.storeconnect.com/)

## Contact

- info@getstoreconnect.com
- US +1 415 745 3230
- AUS +61 2 8365 2308

100 S Ashley Dr, Suite 600-2461
Tampa FL 33602-600 USA

Level 22, Sydney Place
180 George Street
Sydney, NSW, 2000, AUS

## Machine-readable

- [Site index for agents](https://storeconnect.com/llms.txt): curated map of the StoreConnect site in llms.txt format
- [Documentation index for agents](https://support.storeconnect.com/llms.txt): full technical and product documentation map

Every page and article on this site has a Markdown rendering: append `.md` to its URL.

Continue in Markdown: [Help documentation](https://support.storeconnect.com/help-documentation.md) · [Developer reference](https://support.storeconnect.com/developer-reference.md) · [Videos & tutorials](https://support.storeconnect.com/videos-tutorials.md) · [Release notes](https://support.storeconnect.com/release-notes.md)

---

StoreConnect Support — https://support.storeconnect.com/articles/liquid-debugging-and-performance