Shopify Theme App Extensions with Liquid: A Developer's Guide
How to build Shopify Theme App Extensions using Liquid blocks, enabling merchants to add app functionality through the theme customizer.
What Are Theme App Extensions?
Theme App Extensions let Shopify app developers inject UI into merchant themes without editing theme code directly. Merchants add your extension blocks through the theme customizer — no code changes, no compatibility issues.
This is how LogoBadge adds a Google Reviews badge to any Shopify theme.
Why Theme Extensions Over ScriptTag?
The old approach — injecting JavaScript via ScriptTag API — had problems:
- Performance — external scripts slow down page load
- Conflicts — scripts can conflict with theme JavaScript
- Removal — uninstalling the app doesn't always remove the script
- Control — merchants can't position or configure the element
Theme Extensions solve all of these. They render server-side with Liquid, load instantly, and merchants control placement.
Extension Structure
extensions/
logo-badge/
blocks/
badge.liquid
assets/
badge-styles.css
google-icon.svg
locales/
en.default.json
shopify.extension.toml
Configuration File
# shopify.extension.toml
type = "theme"
name = "Logo Badge"
[[extensions.blocks]]
template = "blocks/badge.liquid"
name = "Google Reviews Badge"
target = "section"
Building the Liquid Block
A block is a Liquid template with a schema that defines settings:
{% comment %} blocks/badge.liquid {% endcomment %}
{% if block.settings.show_badge %}
<div class="logobadge"
style="text-align: {{ block.settings.alignment }};
margin: {{ block.settings.margin_top }}px 0 {{ block.settings.margin_bottom }}px;">
<a href="{{ block.settings.maps_url }}"
target="_blank"
rel="noopener noreferrer"
class="logobadge__link">
<span class="logobadge__icon">
{{ 'google-icon.svg' | asset_url | img_tag: 'Google', 'logobadge__google-icon' }}
</span>
<span class="logobadge__rating">
{{ block.settings.rating }}
</span>
<span class="logobadge__stars">
{% assign full_stars = block.settings.rating | floor %}
{% assign decimal = block.settings.rating | minus: full_stars %}
{% for i in (1..full_stars) %}
<span class="logobadge__star logobadge__star--full">★</span>
{% endfor %}
{% if decimal >= 0.5 %}
<span class="logobadge__star logobadge__star--half">★</span>
{% endif %}
</span>
<span class="logobadge__count">
({{ block.settings.review_count }})
</span>
</a>
</div>
{% endif %}
{{ 'badge-styles.css' | asset_url | stylesheet_tag }}
{% schema %}
{
"name": "Google Reviews Badge",
"target": "section",
"settings": [
{
"type": "checkbox",
"id": "show_badge",
"label": "Show badge",
"default": true
},
{
"type": "text",
"id": "rating",
"label": "Google rating",
"default": "4.8"
},
{
"type": "text",
"id": "review_count",
"label": "Number of reviews",
"default": "100"
},
{
"type": "url",
"id": "maps_url",
"label": "Google Maps URL"
},
{
"type": "select",
"id": "alignment",
"label": "Alignment",
"options": [
{ "value": "left", "label": "Left" },
{ "value": "center", "label": "Center" },
{ "value": "right", "label": "Right" }
],
"default": "center"
},
{
"type": "range",
"id": "margin_top",
"label": "Top margin",
"min": 0,
"max": 40,
"step": 4,
"unit": "px",
"default": 8
},
{
"type": "range",
"id": "margin_bottom",
"label": "Bottom margin",
"min": 0,
"max": 40,
"step": 4,
"unit": "px",
"default": 8
}
]
}
{% endschema %}
Updating Settings from Your App
The challenge: Google Reviews data changes over time. How does the badge stay current? The app backend periodically fetches fresh data and updates the block settings via Shopify's Admin API:
async function updateBadgeSettings(shop, accessToken, reviewData) {
const response = await fetch(
`https://${shop}/admin/api/2024-01/themes/${themeId}/assets.json`,
{
method: 'PUT',
headers: {
'X-Shopify-Access-Token': accessToken,
'Content-Type': 'application/json'
},
body: JSON.stringify({
asset: {
key: 'config/settings_data.json',
// Update the specific block settings
}
})
}
);
}
Alternatively, use metafields that the Liquid block reads dynamically:
{% assign rating = shop.metafields.logobadge.rating %}
{% assign review_count = shop.metafields.logobadge.review_count %}
Metafields are updated via the Admin API without touching theme settings:
await fetch(`https://${shop}/admin/api/2024-01/metafields.json`, {
method: 'POST',
headers: {
'X-Shopify-Access-Token': accessToken,
'Content-Type': 'application/json'
},
body: JSON.stringify({
metafield: {
namespace: 'logobadge',
key: 'rating',
value: '4.9',
type: 'single_line_text_field'
}
})
});
Styling Best Practices
Namespace Everything
.logobadge { display: inline-flex; align-items: center; gap: 4px; }
.logobadge__link { text-decoration: none; color: inherit; display: inline-flex; align-items: center; gap: 4px; }
.logobadge__rating { font-weight: 600; font-size: 14px; }
.logobadge__stars { color: #fbbc04; }
.logobadge__count { color: #666; font-size: 13px; }
.logobadge__google-icon { width: 16px; height: 16px; }
Respect Theme Styles
- Use
inheritfor fonts and colors where possible - Don't set global styles that could leak into the theme
- Use CSS custom properties for theming:
.logobadge {
font-family: var(--font-body-family, inherit);
color: var(--color-foreground, #333);
}
Testing Your Extension
# Start development server
shopify app dev
# This opens a development store with your extension loaded
# Navigate to Theme Customizer to add your block
Deployment
Theme extensions are deployed alongside your Shopify app:
shopify app deploy
This pushes the extension to Shopify's CDN. Merchants who have installed your app can then add the block through their theme customizer.
Limitations
- No JavaScript execution in the block itself (use separate ScriptTag if needed)
- Limited to theme sections — you can't inject into arbitrary positions
- Settings are static — for dynamic data, use metafields or App Proxy
- One extension per app — bundle all blocks in a single extension
Conclusion
Theme App Extensions are the modern way to extend Shopify storefronts. They're performant (server-rendered), merchant-friendly (theme customizer integration), and maintainable (no theme code editing). For any Shopify app that needs a storefront presence, Theme Extensions should be your first choice.
Related Project
LogoBadge - Google ReviewsShopify app that automatically displays a Google Reviews badge directly under the store logo, showing live star ratings and review counts from Google Business Profile.