Skip to content

Instantly share code, notes, and snippets.

@reggi
Last active January 23, 2022 21:46
Show Gist options
  • Star 32 You must be signed in to star a gist
  • Fork 5 You must be signed in to fork a gist
  • Save reggi/73b4a8deffd4e0becbb1 to your computer and use it in GitHub Desktop.
Save reggi/73b4a8deffd4e0becbb1 to your computer and use it in GitHub Desktop.

Templating engines and React.js

I want to make a shopify theme using react.

How shopify theming works

You have a bunch of template files that have access to global server-side variables with liquid e.g. {{ product.title }}. Think wordpress or any other theme-based system.

 /theme
   /template
     /index.liquid
     /products.liquid

SEO

  • Because this site is hosted on Shopify I don't have access to the server to render out react on the server dynamically.
  • Because this is an e-commerce store I can't have pages not be rendered server side, for SEO reasons.
  • I need to programmatically generate all of the pages in the template with react render locally, each template would have a component.

Injecting server-side variables into react

It's simple enough to pass the string versions of liquid variables to a component. What I'm not sure of is how to differentiate the props / components that are render out saved within a template and the instance of react in javascript that is running on the site. When the client runs react if the liquid variables are the default properties all the valued server-side variables will be converted to their string / variable counterpart this isn't what we want. React doesn't have the data, this is bad.

To fix this I thought about having two sets of properties, one getLiquidProps that will be utilized when I call something like renderLiquid(). Similar to the way react-async works. However what I really need is a way to read the existing values that the server wrote to the DOM and have those values be the component props. But this is us using the react server DOM as our datastore is this possible?

Example

If I have a simple component for cart counter:

var CartCount = React.createClass({
    getDefaultProps: function(){
     	return {
     		"count": '{{ cart.item_count }}'
     	}
    },
    render: function() {
        return (
            <div className="cartCount">
                { this.props.count }
            </div>
        )
    }
});

The server will render

<div className="cartCount">
  3
</div>

The client-side react will overwrite it again

<div className="cartCount">
  { this.props.count }
</div>
@mohdhazwan
Copy link

Looking forward your progress. Btw found this site https://oliversapparel.com/ but it's not 100% react

@davis
Copy link

davis commented Apr 27, 2016

also looking forward to your progress! i was thinking maybe you would have to request snippets from the shopify store using AJAX, populate the snippet with your own stuff, further making more AJAX calls from your app to shopify store for any globals, such as cart, then using some liquid compiler, doing the compiling of liquid yourself, then rendering...

@ninjasort
Copy link

Cool, I was working on this with metalsmith here but eventually wanted to move it into React. Considering the constraints in local development, you would need to somehow leverage the API as well to speed up local modifications. For example, in this metalsmith library, I fetch all the data from the Shopify API, cache it locally, and re-render the site on every change. Ideally this would be hot-reload with React instead of Metalsmith.

I think you could definitely pull this off. The question becomes, how does one host the server-rendered React build on Shopify? For example, if I built a full app that uses my store's data, how would I then deliver that to a browser with the server-rendered data? A few problems arise:

  1. You unfortunately cannot access all the data from the API, some of the template variables are directly served from Shopify.
  2. Uploading a full React app to Shopify would not be useful for SEO because of the client render.

A crazy approach:

Local development is really all that matters. After uploading files to Shopify, it doesn't really matter what they look like because liquid is messy anyway. So in local development if you could build a fully isomorphic Shopify/React app that Shopify could understand, you could use that on their server.

How do we get Shopify to understand it? We talk in their language - liquid.

When uploading the React app to Shopify, a few things need to happen:

  1. Building an AST (abstract syntax tree) of the React app with the components and their data
  2. Translate that AST to liquid with the corresponding tags and usage
    • use only templates, no includes
    • allocate all settings properties into the correct tags
    • render almost everything as a string into a liquid file
  3. Store the client React bundle in the assets folder
  4. Create a custom upload script that puts the right files into the correct folders on Shopify

With that process, although complex and annoying, you'll be able to build fully isomorphic React/Redux apps for Shopify with 100% SEO and client rendering. That would be sweet!

Thoughts?

@dlindenkreuz
Copy link

Transpiling React code into Liquid is pretty darn hard as Liquid has completely different concepts of e.g. looping over collections, generating HTML tags etc.

I launched a b2b shop (unfortunately non-public) a couple of months ago that uses SSR with React and a technique similar to the one shared by @reggi. I didn't reach feature parity between server-side and client-side templates but this is — so far — better than nothing.

I used https://github.com/postcss/postcss-url for changing all referenced image URLs to Liquid's asset_url tags and some custom gulp scripts to move statically rendered routes like /account/login, collection and product to the respective Liquid files (templates/customers/login.liquid etc.).

For local staging, I grabbed all data I could from Shopify's JSON API, cached it locally and rendered the generated Liquid templates using Koa as local server. To achieve that, I had to implement a couple of Shopify's custom liquid filters in JS and re-organise the JSON data a bit. All of that code is extremely untidy so there won't be any open source library soon. If I should end up developing more shops with that dev setup, I will have to clean things as builds are somehow sluggish (incremental: ~2s, full build: ~50s).

HMA on Twitter (@dlindenkreuz) if you want to know more ¯_(ツ)_/¯

@good-idea
Copy link

Is there any reason why you wouldn't, or aren't able to host it all on your own server and take care of everything solely through the shopify API?

@mgan59
Copy link

mgan59 commented Jun 20, 2017

Was curious what a transpiling of react to liquid might entail, love this idea, so simple. 👍

@paynecodes
Copy link

Anyone come up with something they were happy with here?

@loicnestler
Copy link

loicnestler commented Aug 12, 2021

Hey!
What would be the 2021 approach of having a hybrid react/liquid shopify storefront?

I had the following idea in mind:
To each .liquid page, add a script tag that server side renders all needed liquid variables into a global JavaScript object (window.data or sth).

window.data = {
    count: '{{ cart.item_count }}',
    // ...
}

Liquid should then take care of rendering all content that is SEO-important (landing pages (?), on the product page the product title, description, ...). React is then only used for things that are rather hard to implement in liquid (such as image slider, variant / amount selectors, ...).

<body>
  <h1>{{ product.title }}</h1>
  <p>{{ product.description }}</p>
  <div id="root"></div>
</body>

What do you think of that?

EDIT:
I'm not that familiar with SEO, but it might even be an approach to first render all SEO-relevant content server side using liquid and then, client side, hide the content and mount a whole react application and not just smaller features as described above.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment