What's up with shopify:section:load stacking event listeners?

Topic summary

A developer identified a critical issue with Shopify’s Theme Editor event handling where shopify:section:load event listeners stack with each section reload.

The Problem:

  • Each time a user makes changes in the Theme Editor, a new event listener is added without removing previous ones
  • After 10 changes, “Hello World” logs 10 times because all prior listeners remain active
  • This creates a memory leak scenario where multiple instances of the same code run simultaneously

Concern:

  • With heavy JavaScript libraries, 25 Theme Editor changes would result in 25 active instances in memory
  • The shopify:section:unload event appears insufficient to prevent this stacking behavior

Status: The question remains unanswered—seeking best practices for properly managing event listeners in Shopify’s Theme Editor to prevent memory bloat and duplicate executions.

Summarized with AI on October 26. AI used: claude-sonnet-4-5-20250929.

Following code logs “Hello World” in the Theme Editor each time the user makes a change. When the user makes more than 10 changes, we will get 10 “Hello Worlds” because all the previous event listeners are still active.

<script>
      document.addEventListener( 'DOMContentLoaded', function() {
          console.log("Live")
          
      });

      if (Shopify.designMode) {
          document.addEventListener('shopify:section:load', function(event) {
              if (event.detail.sectionId === '{{ section.id }}') {
                  console.log("Hello World");
              }
          });
          document.addEventListener('shopify:section:unload', function(event) {
            if (event.detail.sectionId === '{{ section.id }}') {
              console.log("Section unload");
            }
          });
        }
</script>
{% schema %}
  {%- comment -%} User-configurable settings go here {%- endcomment -%}
{% endschema %}

What’s up with the logic? What if I’m using a heavy JS library and the user makes 25 changes in TE? We will then have 25 instances in memory!

The reason you see multiple “Hello World” messages is because each time the section is re‑rendered in the Theme Editor, your inline <script> runs again and registers a new event listener. Shopify’s Theme Editor rebuilds sections when you change settings, but previously registered listeners are not cleaned up automatically, so they continue to fire. This is fine when the code is idempotent, but if you’re logging or mounting heavy libraries, you end up with many stacked listeners.

  • Use the shopify:section:unload callback to remove listeners you added earlier. In your example you add a shopify:section:load listener on every refresh but you never remove it. You can store your handler in a variable and call document.removeEventListener('shopify:section:load', handler) in the unload event.

  • Alternatively pass { once: true } as the third argument when adding the listener. This tells the browser to remove the listener automatically after it fires once, so repeated reloads won’t stack up.

  • For non‑designMode use, register listeners outside of the <script> inside the section so they only run once (for example in your main.js file), and inside designMode check if you’ve already attached a listener by setting a flag.

  • If you’re mounting a heavy JS library inside a section (e.g. a carousel or map), instantiate it in shopify:section:load and destroy/cleanup it in shopify:section:unload to release memory. Heavy libraries usually provide a destroy method.

  • Example:

<script>
document.addEventListener('DOMContentLoaded', () => {
  console.log('Live');
});
if (Shopify.designMode) {
  const sectionId = '{{ section.id }}';
  function handleLoad(event) {
    if (event.detail.sectionId === sectionId) {
      console.log('Hello World');
    }
  }

  document.addEventListener('shopify:section:load', handleLoad, { once: true });

  document.addEventListener('shopify:section:unload', (event) => {
    if (event.detail.sectionId === sectionId) {
      document.removeEventListener('shopify:section:load', handleLoad);
      console.log('Section unload');
    }
  });
}
</script>

This pattern ensures that each time the section is loaded the listener is added once and removed when the section unloads, avoiding stacked instances.

1 Like