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 Liquid section or 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.
To add custom JavaScript in Shopify, put section-specific code in the section’s {% javascript %} tag, global code in theme.liquid or a deferred asset file, and quick snippets in a Custom Liquid block in the Theme Editor. Each place has a different scope and performance cost.
Can you add custom code to Shopify?
Yes, Shopify’s code editor is fully accessible. Go to Online Store → Themes, click the three-dot menu (…) next to your theme, then 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 Liquid blocks in the Theme Editor
To build the section that holds your script, see How to Add a New Section Type in Shopify From Scratch.
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:
<countdown-timer class="countdown-section" id="countdown-{{ section.id }}">
<span class="countdown-timer"></span>
</countdown-timer>
{% javascript %}
// This code is:
// - Automatically bundled with other section JS by Shopify
// - Automatically deferred (doesn't block page rendering)
// - Served site-wide in one file with every other section's JS
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, so it doesn’t block rendering
- Shopify bundles the code from every section’s
{% javascript %}tag into one deferred file - The script lives in the same file as the markup it controls
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
To shrink these files before they ship, see How to Minify CSS and JavaScript in Shopify.
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.
For scripts that slow the first render, see How to Fix Render-Blocking Scripts in Shopify.
Method 3 - Custom Liquid block in the Theme Editor
Dawn and most Online Store 2.0 themes include a Custom Liquid section (and block) in the Theme Editor. It accepts HTML, <script> tags and Liquid.
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 Liquid 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 Liquid section is the alternative. In the Theme Editor, add a Custom Liquid section, paste your <script>...</script> code, and save.
For larger or more complex JavaScript, Fudge, the AI operator for Shopify stores, can write it for you: describe the behavior you want in its store editor, and Fudge writes the JavaScript as a proper section {% javascript %} block with good structure, as a draft you can test in preview before it reaches shoppers.
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.
FAQ
Where should I add custom JavaScript - section block or theme.liquid?
Section {% javascript %} block if the JS only applies to one section (countdown timer, image gallery behaviour), so the code lives with the markup it controls. theme.liquid if the JS needs to run on every page (analytics, global utilities). Shopify serves all section JS in one site-wide file, so for heavy section code, a deferred asset file loaded from the section itself keeps it off pages that don't use it. If you'd rather not write the JS yourself, describe what you want to Fudge and it generates the right scoped block in the right file.
Can I use jQuery in a Shopify theme?
Modern themes (Dawn and later) don't include jQuery by default - vanilla JavaScript is the expectation. If you must use jQuery, load it explicitly from a CDN, but check the size impact first. Most jQuery one-liners have one-line vanilla equivalents.
Why isn't my JavaScript running?
Most common reasons: (1) the script is loading before the DOM element it targets exists - wrap in DOMContentLoaded or use defer, (2) a Liquid template error broke the page output, (3) a Content Security Policy in your theme is blocking the script. Open the browser console for errors.
Can I run JavaScript on the Shopify checkout pages?
Not as theme code. No one can edit checkout.liquid any more; Shopify switched it off in 2024-2025. Use custom pixels (Customer events) for tracking and checkout UI extensions for interface changes.
Will custom JavaScript hurt my Lighthouse score?
It can, if added carelessly. The biggest hits come from inline scripts in <head> without defer, large libraries loaded on every page, and JavaScript that runs heavy work synchronously on page load. Use defer, lazy-initialise where possible, and only include code on pages that need it.