Using Respond.js with Shopify

Out of the box, Respond.js just doesn’t work with Shopify due to the latter’s asset delivery system. Fortunately, there is a reasonably straightforward technique we can use to resolve this issue.

The Problem

When I was first working on Bootstrap for Shopify, I noticed a common problem cropping up for folks trying to get Respond.js working on the Shopify platform.

The core problem is that Respond.js requires an additional AJAX request to re-download and re-parse stylesheets. If those stylesheets are hosted on a foreign domain (eg, a CDN), cross-origin rules kick in and prevent that request being made. Setting proper CORS headers won’t help, either, because the browsers we’re trying to target with Respond.js in the first place don’t support that.

The good news is that Respond.js does ship with a cross-domain proxy solution. The bad news is that out of the box, with Shopify’s asset delivery system that proxy solution just doesn’t work.

Because Bootstrap for Shopify uses Bootstrap, and Bootstrap ships with Respond.js, this seemed like a problem I should tackle. (I was also motivated by making amends for flippantly claiming that Respond.js worked on Shopify without properly testing that claim).

The Solution

1. Add core Respond.js library

Ensure you’ve added the core Respond.js javascript library (not the cross-domain library) to your Shopify template and that it’s linked correctly. It should look something like this in your theme.liquid:

<!--[if lt IE 9]>
<script src="{{ 'js-respond-min.js' | asset_url }}"></script>
<![endif]-->

2. Add proxy HTML file

Download the cross-domain proxy HTML file respond-proxy.html and copy it to your Shopify theme’s assets folder.

Now link to that proxy HTML as suggested by the Respond.js docs, but with a twist, like so:

<link href="{{ 'respond-proxy.html' | asset_url | split: '?' | first }}" id="respond-proxy" rel="respond-proxy" />

The asset_url filter links to the proxy HTML file on the same domain as our stylesheets (the Shopify CDN), while the split and first filters remove the ?1234 query parameter that Shopify adds to asset URLs. That query parameter prevents browsers caching and using outdated assets, but also breaks the default implementation of Respond.js’s cross-domain proxy.

3. Add Respond.js proxy Javascript as a template

Per the proxy instructions, we need to host two files - respond.proxy.js and respond.proxy.gif on the same domain that our page is being served from. This is problematic, because Shopify won’t let us link to assets that aren’t hosted on the Shopify CDN, and we can’t just link to the assets on an external server because we get back to the whole cross-domain issue that led us down this path in the first place.

So, what can we do? The only content we can serve from the same origin hostname as our own page is HTML content - so, we’re going to do just that, but ask the browser to interpret it as Javascript. Copy the contents of respond.proxy.js into a new file named page.respond-js.liquid, and save it in the templates directory of your Shopify theme. Before you close it, edit the end of the file so that it looks like this:

...
})( window, document );
{% layout none %}

We’ve just created a page template that contains Javascript content (the layout none tag tells Shopify not to add regular wrapping HTML when serving the page) that will be served from the same domain as our regular pages.

4. Create a page using the new template

We now need to create a page that uses this new template, so that we can fetch that Javascript. Open up your Shopify administration page, go to the “Blogs and Pages” tab, and create a new, blank page. Give it any title, note the URL the new page is going to live at, and ensure that it’s set to use the page.respond-js template. Save it - you’re nearly done!

5. Link to the new proxy asset

The final step is to link to the Javascript asset we just created. Add the following two lines to your theme.liquid, just below your link to respond-proxy.html.

<link href="{{ shop.url }}/pages/respond-js" id="respond-redirect" rel="respond-redirect" />
<script src="{{ shop.url }}/pages/respond-js" type="text/javascript"></script>

The snippet above assumes that the page you created that uses the page.respond-js template is accessible at /pages/respond-js.

Note that all of the <script> and <link> tags listed above should be wrapped in the <!--[if lt IE 9]> directive, as we only want to target browsers that don’t support media queries natively. So, the final code in your <head> should look something like this:

<!--[if lt IE 9]>
<script src="{{ 'js-respond-min.js' | asset_url }}"></script>
<link href="{{ 'respond-proxy.html' | asset_url | split: '?' | first }}" id="respond-proxy" rel="respond-proxy" />
<link href="{{ shop.url }}/pages/respond-js" id="respond-redirect" rel="respond-redirect" />
<script src="{{ shop.url }}/pages/respond-js" type="text/javascript"></script>
<![endif]-->

6. Profit.

That’s it! If you’ve set everything up correctly, you should now see Respond.js effecting responsive behaviour in older browsers.


Update: The original article used {{ canonical_url }} instead of {{ shop.url }} at step 5 — this was causing issues and has been corrected. Thanks to Adam Moore at Blast and Caroline Schnapp at Shopify for their feedback.

Heads Up: I’ve written a follow-up to this article, which goes over some common pitfalls and introduces a new technique for serving the cross-domain script without using a page template. Read the sequel.