Key takeaways
- The best place for section-specific JavaScript is the
{% javascript %}block inside the section’s Liquid file — Shopify bundles and defers it automatically.- Global JavaScript that runs on every page goes in
theme.liquidbefore</body>, ideally with adeferattribute.- The Custom HTML block in the Theme Editor accepts inline
<script>tags — useful for quick snippets without code editor access.- Can you add custom code to Shopify? Yes — the code editor is fully accessible to anyone with the “Manage themes” permission.
JavaScript powers the interactive parts of your Shopify store — dynamic cart updates, custom product configurators, scroll animations, and more. Shopify gives you several places to add JavaScript, each with different scope and performance implications.
Can you add custom code to Shopify?
Yes, Shopify’s code editor is fully accessible. Go to Online Store → Themes → Actions → Edit code to browse and edit all theme files. You can add JavaScript in several ways:
- Section
{% javascript %}blocks - Global
theme.liquid - Standalone
.jsfiles in theassets/folder - Custom HTML blocks in the Theme Editor
Method 1 — Section {% javascript %} blocks (recommended)
The best practice for any JavaScript that’s specific to one section.
In your section file (sections/your-section.liquid), add a {% javascript %} block:
<div class="countdown-section" id="countdown-{{ section.id }}">
<span class="countdown-timer"></span>
</div>
{% javascript %}
// This code is:
// - Automatically bundled with other section JS by Shopify
// - Automatically deferred (doesn't block page rendering)
// - Only compiled when the section is actually used on a page
class CountdownTimer extends HTMLElement {
connectedCallback() {
this.startCountdown();
}
startCountdown() {
// countdown logic
}
}
if (!customElements.get('countdown-timer')) {
customElements.define('countdown-timer', CountdownTimer);
}
{% endjavascript %}
Why this approach is best:
- Shopify automatically defers section JavaScript — no render blocking
- Code only loads on pages where the section is actually used
- Multiple sections’ JavaScript is bundled into fewer requests
Important: Variables in {% javascript %} blocks cannot access Liquid variables directly. If you need Liquid data in your JS, output it as a data attribute or a JSON script tag in the HTML part of the section.
Passing Liquid data to JavaScript:
<div
class="product-countdown"
data-sale-end="{{ section.settings.sale_end_date }}"
data-product-id="{{ product.id }}"
></div>
Then in your JavaScript:
var el = document.querySelector('.product-countdown');
var saleEnd = el.dataset.saleEnd;
var productId = el.dataset.productId;
Method 2 — theme.liquid (global JavaScript)
For JavaScript that must run on every page of your store, add it to theme.liquid.
Before </body> with defer (non-critical global scripts):
<script src="{{ 'custom-global.js' | asset_url }}" defer></script>
Inline script in <head> (only for critical, tiny scripts):
<script>
// Keep this extremely small — it blocks page rendering
window.__store = { currency: '{{ shop.currency }}' };
</script>
When to use theme.liquid for JavaScript:
- Third-party integrations that need to initialize on every page (GTM, analytics)
- Global utility functions used by multiple sections
- Store configuration that sections need before they initialize
Always use defer or async on external script references in theme.liquid. Plain <script src="..."> tags in <head> are render-blocking.
Method 3 — Custom HTML block in the Theme Editor
Some themes include a “Custom HTML” section type in the Theme Editor. This accepts any HTML, including <script> tags.
When to use:
- Quick, one-off JavaScript snippets you want to add without the code editor
- Third-party embeds that provide a snippet (chat widgets, social plugins)
- Testing something quickly before committing it to a section file
Limitations:
- Scripts in Custom HTML blocks are inline and not deferred automatically
- Not the right place for significant JavaScript — use section
{% javascript %}blocks instead
How to add custom JS without the code editor
If you don’t have code editor access (or prefer not to use it), the Custom HTML section block is the alternative. In the Theme Editor, add a Custom HTML section, paste your <script>...</script> code, and save.
For larger or more complex JavaScript, Fudge is the better approach — describe the behavior you want, and Fudge writes the JavaScript as a proper section {% javascript %} block with good structure.
Loading JavaScript from the assets/ folder
For reusable JavaScript that multiple sections reference:
- Go to code editor →
assets/folder → create a new.jsfile - Write your JavaScript in the file
- Reference it in theme.liquid or a section:
<!-- In theme.liquid or a section's HTML -->
<script src="{{ 'your-script.js' | asset_url }}" defer></script>
This approach works well for utility libraries or shared functionality, but the {% javascript %} block approach is preferred for section-specific code because it’s automatically bundled.
Common mistakes with Shopify JavaScript
Using document.write(). Not supported in deferred scripts and deprecated broadly.
Assuming jQuery is available. Modern Shopify themes (Dawn and later) don’t include jQuery. Write vanilla JavaScript instead.
Accessing Liquid variables directly in {% javascript %} blocks. This doesn’t work — Liquid is processed server-side before JavaScript executes. Pass data via data attributes instead.
Script tag in <head> without defer/async. Every millisecond of JavaScript parse time in <head> delays your page. Always defer non-critical scripts.
Does Shopify support JavaScript?
Yes. Shopify themes are built with HTML, CSS, Liquid (Shopify’s templating language), and JavaScript. You have full access to the browser’s JavaScript APIs, and can use any JavaScript library by loading it from your assets folder or a CDN. Shopify also provides its own JavaScript APIs — Shopify.currency, the Ajax Cart API, and the Section Rendering API — for common storefront interactions. The one constraint is that server-side JavaScript (Node.js) doesn’t run in Shopify themes; all JS in themes executes in the browser.