Code splitting (modular) of javascript file inside Theme app extension block

Topic summary

Core Issue:
Developers struggle to split JavaScript code into multiple files within Shopify Theme App Extensions, as the architecture restricts custom folders and standard ES6 imports don’t work natively.

Initial Workaround (Posts 2-6):

  • One developer shared a solution using Rollup.js as a bundler to compile multiple JS files into the extensions folder.
  • A blog post tutorial was provided explaining the implementation.
  • Important note: requires adding "type": "module" to package.json.
  • For multiple app blocks, the solution was extended using SystemJS loader with modified rollup.config.js to handle separate JS files per block.

Simpler Native Solution (Posts 7-10):

  • A developer pointed out that native ES6 modules work without bundlers:
    • Use export in helper files and import in main files
    • Add type="module" to script tags in .liquid files
    • For CSS: use @import url('./other.css') in main stylesheet
  • This approach was confirmed working by multiple users, suggesting Shopify may have fixed earlier limitations.

Remaining Challenge (Post 11):
Dynamic imports face issues with Shopify’s asset versioning system (appended ?v= parameters), causing SystemJS to load incorrect file versions.

Status: Multiple working solutions exist, with the native ES6 approach being simplest for basic code splitting.

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

Hi,

I’m trying to set something similar up with my shopify theme but I’ve run into a snag when it comes to dynamic imports.

It’s hard to explain, but basically I want a big chunk of code to load but only when the user clicks a button.

The only neat way of doing this with rollup and everything else is to use dynamic es imports in my source code, which gets converted into SystemJS.import()s.
However, Shopify likes to append a long string of numbers to version every time you upload an asset (ie, code).
SystemJS doesn’t know this and tries to load the original version of the uploaded script, which has long since changed. Not only that but it also has a dependency check that causes it to also load the original uploaded version of the calling script!

Those ?v= arguments are important.

Shopify never make it easy…

I could change it so rollup outputs the files using the default template [name]-[hash].js, but this doesn’t address the issue of SystemJS attempting to load dependencies (app.system.js) because it doesn’t recognize that ‘app.system.js?v=38838934239889749121708712258’ is the actual dependency and is already loaded.

Any ideas?

EDIT

The problem is a bit worse than I thought. forget dynamic imports.

I have a component called ‘flipdown’ which is embedded in its own section type.

In sections/flipdown.liquid:


{{ "flipdown.css" | asset_url | stylesheet_tag }}

But even this ends up downloading and running the original uploaded version of app.system.js