Shopify / Shopify/liquid

Request for Improvements: Enhanced Cache Control in Liquid

Open
#1,857 1 comment 9 reactions 0 assignees View on GitHub

Nobody has claimed this yet.

Feature Request
Dominant language
Ruby
Stars
11.9k
Forks
1.5k
Avg merge
17h 55m
Merged PRs (30d)
3

Description

Background

Optimising Time to First Byte (TTFB) plays a role in improving website performance and user experience on Shopify stores. Complex Liquid code, particularly in sections and snippets involving deeply nested loops and extensive logic, can significantly impact TTFB. Common examples include highly nested mega menus or mobile drawers that, while relatively static, are generally unoptimisable without bodges.

Other frameworks, such as Django, offer granular cache control mechanisms that allow developers to cache specific parts of a template. Introducing similar cache control features in Shopify Liquid would enable merchants and developers to improve rendering times and have greater control over how elements of their store are cached.

Proposal
Introduce Cache Tags for Sections and Snippets
  • Implementation of {% cache %} and {% endcache %} tags. Allow developers to wrap sections of Liquid code that should be cached, minimising the need to reprocess unchanged content.
  • Cache Duration and Key Management. Provide options to set cache lifetimes and specify keys, enabling cache invalidation.
Limitations

Implementing per-section or per-snippet caching will come with distinct limitations:

  • Restricted Objects: Caching would not apply to code containing dynamic objects like customer or cart. This restriction would need to be enforced at the SFR level and flagged in Theme Check to prevent improper caching attempts.
  • Theme Changes: Automatic invalidation of cache upon:
    • Theme template edits.
    • Publishing of a new theme.
Challenges with Existing Cache Handling

Opaque SFR Caching Mechanisms
The current SFR caching process lacks any documentation, with most knowledge being informally passed through developers. Tools like the Shopify Theme Inspector help identify issues, but unresolved cases force developers to resort to client-side rendering.

Client-Side Rendering - This adds complexity, disrupts clean server-side logic, and is less optimal for SEO. While search engines have improved at parsing client-side content, server-rendered content remains preferable.

Cache Miss Triggers
It's too easy to move a session into a x-cache: Miss state. For example:

  • A cart being created, even for something as trivial as setting a cart attribute. Popular analytics and tracking tools exacerbate this by setting cart attributes upon the first page view, leaving many buyers in a cache-less state after their first page view. The values are always unique to the session, and as such the cache is always unique to the session.

This in itself is probably an issue that could be looked at separately. Is there a path where these apps can set attributes that are not tied to the pages rendering lifecycle?

Additional Thoughts
  • Improved caching would not necessarily revolutionise store performance but would serve as a valuable tool for large merchants and complex stores.
  • Documentation on SFR caching is necessary to support developers in leveraging caching effectively. This would also empower app developers who extensively use cart attributes to make more informed decisions about the impact of using cart attributes.
Example Usage
{% cache %}

  <div class="image-banner">
      <!-- Generic image banner from Dawn, not using any dynamic sources with a few settings set -->
  </div>

{% endcache %}

Within the given context of the current theme, and current theme template, this would be cached until SFR's default cache expiry is met. Currently an image banner from Dawn with a few settings set takes ~30ms to render. Could this be pulled from an edge cache faster?

{% cache: expires_in: 3600 %}

  <nav class="mega-menu">
      <!-- Complex nested logic for mega menu with images & custom title parsing -->
  </nav>

{% endcache %}

Within the given context of the current theme, and current theme template, this would be cached for 60 minutes. Changes to the contents of the mega menu would not be presented via the SFR until the cache object is marked as stale.

{% cache: key: section.settings.menu %}

  <nav class="mega-menu">
      <!-- Complex nested logic for mega menu with images & custom title parsing -->
  </nav>

{% endcache %}

Within the given context of the current theme, and current theme template, this would be cached until the computed hash of the menu object changes, or when the SFR's default cache expiry is met - Whatever comes sooner.

{% cache: key:card_product.updated_at %}

  <div class="product-card">
      <!-- Complex product card, with product swatches / options & multiple images  -->
  </div>

{% endcache %}

Within the given context of the current theme, and current theme template, this would be cached until the updated_at field changes, or when the SFR's default cache expiry is met - Whatever comes sooner. Please note: updated_at is not currently available under the product object within Liquid.

{% cache: key: card_product %}

  <div class="product-card">
      <!-- Complex product card, with product swatches / options & multiple images  -->
  </div>

{% endcache %}

Within the given context of the current theme, and current theme template, this would be cached until a computed hash of the product object changes, or when the SFR's default cache expiry is met - Whatever comes sooner.

{% cache %}

    <nav class="mega-menu">
      <p> {{ customer.first_name }}</p>
      <!-- Complex nested logic for mega menu with images & custom title parsing -->
  </nav>

{% endcache %}

This would not be cached since the customer object is referenced within the cache block. This would also raise a Theme Check error.

Contributor guide

Open the contributing guide

First steps

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. Open a pull request that references the issue number.

Research direction

No files, tests, or entry points are identified. Start by reviewing the existing SFR caching behavior and Liquid's current tag capabilities, then establish the scope for cache tags, duration and key handling, invalidation, and dynamic-object restrictions. Done requires an agreed design, implementation plan, and documentation needs.

Written by the indexing model from the issue text.

Assessment

Tech stack
ruby
Domain
performance, web-dev
Issue type
Feature
Difficulty
5/5
Estimated time
Over a week
Activity status
Stale
Clarity
Needs clarification
Newbie friendliness
25/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.