Skip to main content

Welcome to a Grasp lesson

Create a free account to keep studying and create your own study path.

Lesson 64

Building a Theme App Extension for Product Page Customization

Recommended resources

Hello! Welcome back to our capstone project journey.

In the last lesson, we built the crucial backend logic that allows a merchant to save a customization configuration for a product. We used the Admin API to write that configuration to an app-owned product metafield. Now, we have the "what" (the customization options) stored, but we need to address the "how"—how does a customer see and interact with these options?

This lesson directly tackles that question. Our goal is to create a Theme App Extension to display the customization form on the product page. We'll bridge the gap between your app's backend data and the customer-facing storefront. This is where your app's functionality becomes visible and tangible to the end-user.

From Hacking Themes to Clean Integration: Why Theme App Extensions?

To appreciate Theme App Extensions, it's helpful to understand the problem they solve, which aligns with your preference for a "first principles" approach.

For years, if an app needed to add a UI element to a store's theme (like a reviews widget, a wishlist button, or our customizer form), the process was messy. It often involved asking merchants to manually copy and paste snippets of Liquid, HTML, and JavaScript into their theme files. This was problematic:

  • It was fragile: A small mistake in copying could break the entire storefront.
  • It created "ghost code": When the merchant uninstalled the app, leftover code snippets often remained, cluttering the theme and sometimes causing errors.
  • It was difficult to manage: It was hard to track where the app's code was and how it interacted with the theme.

Theme App Extensions are Shopify's modern solution to this problem. They provide a standardized, secure, and sandboxed way for apps to inject content and functionality directly into a theme, all managed through the familiar Theme Editor interface.

Shopify Theme App Extensions (For Theme Developers)

To get a clear picture of this evolution, the video 'Shopify Theme App Extensions (For Theme Developers)' from Code with Chris the Freelancer provides excellent context on the problems solved by this new approach.

Watch the first 3 minutes and 50 seconds. Pay close attention to the explanation of the 'old way' using the Asset API versus the new, cleaner approach with App Blocks and App Embed Blocks.

This diagram illustrates the modern architecture. Your app's extension, the theme, and the store's data are all processed by Shopify's Liquid renderer to create the final, seamless storefront page.

Shopify Theme App Extension Integration Diagram
This diagram shows how your Theme App Extension, containing App Blocks, is rendered alongside the store's theme to create the final storefront page.

App Blocks vs. App Embed Blocks

There are two main types of theme app extensions. Understanding their different purposes is key.

  1. App Blocks: These are for adding inline content that becomes part of the page's layout. Merchants can add, remove, and reposition these blocks within sections using the Theme Editor, just like native theme sections. Our product customizer form is a perfect use case for an App Block, as we want it to appear within the product information area.

  2. App Embed Blocks: These are for functionality that doesn't have a specific inline location or needs to run on every page. This is often used for floating elements (like a chat widget) or for injecting scripts (like an analytics tracker) into the <head> or <body> of the theme. Merchants enable or disable these globally in the Theme Editor.

Understanding Shopify theme app extensions: What they are ...

The article 'Understanding Shopify theme app extensions' from Gadget provides a concise summary of these two types.

Read the two short subsections on 'App blocks' and 'App embed blocks'. This will solidify your understanding of their distinct roles.

Test your understanding!

A merchant wants to use an app that adds a "Recently Viewed Products" bar that floats at the bottom of the screen on all pages. Which type of Theme App Extension should the app developer use for this feature and why?

Show answer

The developer should use an App Embed Block. This is because the feature is a floating element, not inline content, and it needs to appear globally across all pages of the store, not just in a specific section on a single page.


Hands-On: Building the Customizer App Block

Now, let's get practical. We will create an App Block that reads the metafield data we saved in the previous lesson and renders our customization form.

Step 1: Generate the Extension with Shopify CLI

Your app project already has the necessary setup. We just need to tell the Shopify CLI to generate a new extension within it.

  1. Open your terminal and navigate to your app's root directory.
  2. Run the following command:
    npm run generate extension
    
    (Note: depending on your Shopify CLI version, the command might be shopify app generate extension)
  3. The CLI will prompt you to choose the type of extension. Select Theme app extension.
  4. Give your extension a name. Something like product-customizer-form is descriptive.

This process creates a new directory inside your project, typically at extensions/product-customizer-form, populated with boilerplate files.

Step 2: Explore the File Structure

Inside the new extension directory, you'll find a structure that looks a lot like a mini-theme:

  • blocks/: This is where your main Liquid files live. Each file in here represents a unique App Block that your app provides. The CLI generates a star_rating.liquid example. We'll rename and edit this.
  • assets/: For any CSS, JavaScript, or images your extension needs.
  • locales/: For translation strings.
  • shopify.extension.toml: The configuration file for this specific extension.

Step 3: Create the Customizer Form Block

This is the core of today's lesson. We will modify the generated Liquid block to render our form.

First, rename the generated file extensions/product-customizer-form/blocks/star_rating.liquid to customizer_form.liquid.

Now, open this file. It contains both a Liquid/HTML part and a {% schema %} part. Let's replace the entire content with our own logic.

{% comment %}
  This block will read the customizer configuration from a product metafield
  and render the necessary input fields for the customer.
{% endcomment %}

{% liquid
  assign customizer_options_metafield = product.metafields.customizer.options
%}

{% if customizer_options_metafield.value != blank %}
  <div class="product-customizer-container">
    <h3 class="product-customizer-title">{{ block.settings.title }}</h3>
    {% for option in customizer_options_metafield.value %}
      <div class="customizer-field">
        <label for="customizer-{{ option.id }}">{{ option.label.value }}</label>
        
        {% if option.type.value == 'text' %}
          <input 
            type="text" 
            id="customizer-{{ option.id }}" 
            name="properties[{{ option.label.value | escape }}]"
            required>
        
        {% elsif option.type.value == 'dropdown' %}
          <select 
            id="customizer-{{ option.id }}" 
            name="properties[{{ option.label.value | escape }}]"
            required>
            {% assign dropdown_options = option.dropdown_options.value | split: ',' %}
            {% for dropdown_option in dropdown_options %}
              {% assign trimmed_option = dropdown_option | strip %}
              <option value="{{ trimmed_option }}">{{ trimmed_option }}</option>
            {% endfor %}
          </select>
        {% endif %}

      </div>
    {% endfor %}
  </div>
{% endif %}


{% schema %}
{
  "name": "Product Customizer",
  "target": "section",
  "stylesheet": "app.css",
  "javascript": "app.js",
  "settings": [
    {
      "type": "text",
      "id": "title",
      "label": "Form Title",
      "default": "Customize Your Product"
    }
  ],
  "enabled_on": {
    "templates": ["product"]
  }
}
{% endschema %}

Let's break down the key parts of this code:

  • Reading the Metafield: {% assign customizer_options_metafield = product.metafields.customizer.options %} fetches the list of metaobject references we saved in the last lesson.
  • Looping Through Options: {% for option in customizer_options_metafield.value %} iterates through each Customizer Option metaobject assigned to the product.
  • Dynamic Rendering: The {% if option.type.value == 'text' %} logic checks the type field of our metaobject and renders the correct HTML input. This is the power of the data model we designed earlier.
  • Line Item Properties: The name="properties[...]" attribute is a special Shopify convention. Any form fields submitted with this naming pattern will be captured and saved as line item properties on the order. This is how we'll retrieve the customer's choices later. We'll dive deeper into this in the next lesson.
  • The Schema:
    • "name": "Product Customizer": This is the name the merchant will see in the Theme Editor.
    • "target": "section": This defines it as an App Block that can be placed inside a theme section.
    • "enabled_on": { "templates": ["product"] }: This is a crucial rule. It restricts this block so it only appears as an option on product pages.

Step 4: Preview and Test in the Theme Editor

With the code in place, it's time to see it in action.

How to build your Shopify theme app extension | App Embed Blocks and App Blocks

The video 'How to build your Shopify theme app extension' by Ilias Haddad provides an excellent hands-on demonstration of modifying a generated block and adding it to the theme editor. We'll watch the segment where he builds a custom pricing button, as the process is identical to what we need to do.

Watch from 09:20 to 16:30. Observe how he modifies the schema and Liquid code, and then navigates the theme editor to add his new block to a product page. You will follow these exact same steps to add your 'Product Customizer' block.

Follow these steps, as shown in the video:

  1. Make sure your local development server is running with npm run dev.
  2. The CLI will give you a URL to your store. Open it and navigate to Online Store > Themes.
  3. Click Customize on your development theme (e.g., Dawn).
  4. Using the dropdown at the top of the editor, navigate to Products > Default product. Make sure you're viewing a product for which you have already configured customizer options in your app's admin UI.
  5. On the left-hand panel, under a section like "Product information," click Add block.
  6. Scroll down to the "APPS" category. You should see your Product Customizer block. Click to add it.
  7. Use the drag handles to position the form where you want it (e.g., above the "Add to cart" button).
  8. Click Save.

You should now see your customizer form rendered on the product page, dynamically generated from the metafields you configured!


Conclusion

Excellent work! You have successfully created a bridge between your app's data and the live storefront. You've seen how Theme App Extensions provide a clean, robust, and merchant-friendly way to inject UI into a theme, a massive improvement over older methods.

Key Takeaways:

  • Theme App Extensions are Shopify's modern solution for integrating app UI into themes.
  • App Blocks are used for inline content that merchants can place within sections (perfect for our form).
  • App Embed Blocks are for global, non-inline functionality like scripts or floating widgets.
  • The development process involves:
    1. Generating the extension with the Shopify CLI.
    2. Writing Liquid code in the blocks directory to read Shopify data (like our product metafields) and render HTML.
    3. Configuring the block's {% schema %} to set its name, target, and display rules (enabled_on).
    4. Using the Theme Editor to add and position the block on the storefront.

Next Steps

Our form is now visible on the product page, but it doesn't do anything yet. If you fill it out and click "Add to cart," the custom information is lost.

In our next lesson, we will solve this. We will dive into the Shopify Ajax API. You'll learn how to use client-side JavaScript to intercept the "add to cart" action, bundle the customer's selections from your form as line item properties, and add the product to the cart without a full page reload.