Kirill Platonov
Kirill Platonov
Sep 22, 2021

Detecting Shopify theme version in Rails

When building theme app extensions you’ll definitely need a way to detect the version of the theme (Vintage or Online Store 2.0). Because the integration for every version will be different.

The official Shopify docs provide only instruction for Node.js. So I’d like to share the approach for Rails that I was using in my latest FAST Product Color Swatch app.

To detect the version I use shopify_api and extend Theme object by adding #version method. I found this solution the cleanest and easy to use. Doing the same in GraphQL will be extremely verbose so I stick with REST and shopify_api for this task.

You’ll need to add the following code to config/initializers/shopify_app.rb:

class ShopifyAPI::Theme
  # Specify templates required by the app (where app block will be installed).
  # E.g. %w[product collection index]
  APP_BLOCK_TEMPLATES = %w[product]

  def assets
    ShopifyAPI::Asset.find(:all, params: { theme_id: })

  def version
    @version ||= begin
      theme_assets = assets
      template_json_files = do |asset|
        APP_BLOCK_TEMPLATES.any? { |t| asset.key ==  "templates/#{t}.json"}
      return :vintage if template_json_files.size != APP_BLOCK_TEMPLATES.size

      template_main_sections = do |template|
        asset = ShopifyAPI::Asset.find(template.key, params: { theme_id: })
        json = JSON.parse(asset.value)

        main = json["sections"].find { |k, v| k == "main" || v["type"].start_with?("main-") }
        if main
          theme_assets.find { |asset| asset.key == "sections/#{main[1]["type"]}.liquid" }
        elsif template.key == "templates/index.json"
          theme_assets.find { |asset| asset.key == "sections/apps.liquid" }

      sections_with_app_block = do |asset|
        accepts_app_block = false
        file = ShopifyAPI::Asset.find(asset.key, params: { theme_id:} )
        match = file.value.match(/\{\%[-\s]+schema[-\s]+\%\}([\s\S]*?)\{\%[-\s]+endschema[-\s]+\%\}/m)
        schema = JSON.parse(match[1])
        if schema && schema["blocks"]
          accepts_app_block = schema["blocks"].any? { |b| b["type"] == "@app" }

        accepts_app_block ? file : nil

      if sections_with_app_block.size

You can customize which templates should be checked for app blocks support in APP_BLOCK_TEMPLATES constant. It will be different for every app. In my app users can only add app block to the product page so I need to check only product template for blocks support.

After adding this code you can use it like this:

theme = ShopifyAPI::Theme.find(THEME_ID)
# => :os2 or :vintage

Very easy. I like that solution and I wish we could add this code to shopify_api gem eventually. In the next articles, I will tell more about my experience building theme extension.

If you like reading about Shopify app development follow me on Twitter.