window.Shopify.routes.root — Why Every Shopify AJAX Call Must Use It
Your Shopify theme works perfectly on a standard store. Then a merchant enables Shopify Markets with international domains or URL prefixes — /en-us/cart, /fr-fr/cart, /de-de/cart. Every AJAX call to /cart/add.js, /cart.js, and /search/suggest returns 404. The cause: hardcoded URL paths. Shopify provides window.Shopify.routes.root for exactly this scenario — a Liquid-injected base path that automatically includes any market prefix. Use it for every API call.
What happens with localized Shopify Markets
When a merchant enables Shopify Markets with subfolder URL structure, all store URLs receive a locale prefix: /en-us/ for English US, /fr-fr/ for French France, etc. The Shopify cart API, search API, and predictive search API all require this prefix in localized contexts. A call to /cart/add.js in an /en-us/ context must go to /en-us/cart/add.js. Hardcoded paths without the prefix return 404 errors in localized markets — silently breaking all cart interactions for international visitors.
How to inject routes.root into your theme
In your theme.liquid, inject the routes object into a global JavaScript variable: <script>window.Shopify = window.Shopify || {}; window.Shopify.routes = window.Shopify.routes || {}; window.Shopify.routes.root = '{{ routes.root }}';</script>. The routes.root Liquid variable outputs the correct prefix for the current market — an empty string on default English, /en-us on US English subfolder, /fr-fr on French subfolder. Many Shopify themes already do this — check before duplicating.
Using routes.root in every API call
Prepend window.Shopify.routes.root to every Shopify API URL: fetch(`${window.Shopify.routes.root}/cart/add.js`, {...}). fetch(`${window.Shopify.routes.root}/cart.js`). fetch(`${window.Shopify.routes.root}/cart/change.js`, {...}). fetch(`${window.Shopify.routes.root}/search/suggest.json?q=${query}&type=product`). fetch(`${window.Shopify.routes.root}${productPath}.js`). This pattern works on all markets, all locale configurations, and all URL structures without any conditional logic.
Using Liquid routes object for other URL types
Shopify's Liquid routes object provides prefixed paths for all common URLs: routes.root_url (the root path), routes.cart_url (cart page), routes.cart_add_url (/cart/add), routes.cart_change_url (/cart/change), routes.cart_update_url (/cart/update), routes.search_url (/search), routes.predictive_search_url (/search/suggest). Inject all needed routes into window.Shopify.routes at theme initialization for use throughout your JavaScript.
The fix: before and after
// CODE_COMPARISON
Frequently asked questions
- Does window.Shopify.routes.root have a trailing slash?
No. routes.root outputs the prefix without a trailing slash. On a standard store it is an empty string. On an /en-us/ subfolder store it is '/en-us'. Always construct URLs as: `${routes.root}/cart/add.js` — the leading slash before the path ensures correct URL formation in both cases.
- Do I need window.Shopify.routes.root if my store does not use Shopify Markets?
You should use it regardless. Merchants can enable Shopify Markets at any time without warning, and adding market-based URL prefixes after the fact would immediately break any hardcoded paths. Building with routes.root from the start costs nothing and future-proofs your theme against this common merchant configuration change.
- What is the difference between routes.root and routes.root_url in Liquid?
In Shopify Liquid, routes.root and routes.root_url both provide the root path prefix. For JavaScript injection, use {{ routes.root }} (without _url) as it outputs the bare path string (/en-us) without any domain. The routes.root_url variant may include the full domain in some contexts, which is not what you want for relative URL construction in JavaScript.
// SCAN_YOUR_CODE
Does your theme have this bug?
Paste your code. Syphio automatically detects and fixes this error and hundreds of others — in seconds.
Check my JavaScript →