15 Liquid Snippets to Replace Shopify Apps (2026)

I audited 47 Shopify stores last quarter. 41 of them paid for an app that 28 lines of Liquid would replace. One client was paying Bold $19/month for a countdown timer rendering inside a 312KB script bundle.

TL;DR: Replace 5 to 8 frontend-only Shopify apps (announcement bars, countdown timers, trust badges, size charts, free shipping bars, stock indicators, schema, recommendations) with the 15 server-rendered Liquid snippets below. Typical saving: $200 to $500/month, 150 to 500KB of JavaScript stripped per page, 1 to 3 seconds off mobile load.

Why this matters for your store

  • App fees: a typical Hextom + Bold + Privy + Sticky Tools + ReConvert stack costs $87/month, or $1,044/year per store.
  • Performance: each app injects 50 to 200KB of JavaScript on every page, and apps run in the head where they block your LCP.
  • Risk: when an app gets acquired, sunsetted, or breaks after a Shopify theme update, your PDP breaks with it.

What an app actually costs you

Every installed app has three line items beyond the invoice.

Performance. On a WD Electronics audit in March 2026, three apps (Bold Upsell, Hextom Banner, Smart Trust Badges) added 2.3 seconds to mobile Time to Interactive. PageSpeed mobile was 41. After replacing all three with the snippets below, the score jumped to 78. To find your own bloat, run my Shopify App Bloat Detector. It scans 192 known apps and ranks by Total Blocking Time.

Maintenance. Apps inject CSS that overrides your design system. They conflict with each other. When Hextom rewrote its banner script in 2024, three of my client stores lost their announcement bar overnight.

Control. Custom Liquid means you own the markup, the styling, the timing. No black-box bundle phoning home from a vendor’s CDN.

App category Replace with Liquid? Typical cost Why
Announcement bars, trust badges, countdown timers (Hextom, Bold, Sticky Tools) Yes $0 to $15/mo Pure frontend UI
Size charts, product tabs, free shipping bars (Kiwi, EasyTabs, Hextom) Yes $5 to $15/mo Data lives in metafields
Stock indicators, recently viewed, cross-sells (Beeketing, LimeSpot) Yes $5 to $30/mo Native Shopify objects + small JS
Product JSON-LD, responsive images (Smart SEO, JSON-LD for SEO) Yes $5 to $30/mo Liquid renders server-side
Email automation, abandoned cart (Klaviyo, Omnisend) Keep $20 to $150/mo Needs deliverability + automation
Subscriptions, tax (Recharge, Avalara) Keep Revenue share Needs Shopify-approved billing

Shopify App Store category page showing trust badge and social proof apps with monthly subscription fees

How the 15 snippets cut your stack

Each snippet below is copy-paste ready. Code blocks are trimmed for readability. Full versions live in production stores I’ve shipped, including Factory Direct Blinds and Mobelglede. For the underlying Liquid patterns, start with my Shopify Liquid development guide.

1. Announcement bar (replaces Hextom, $9.99/mo)

{% comment %} sections/announcement-bar.liquid {% endcomment %}
{%- if section.settings.text != blank -%}
<div class="announcement-bar" id="announcement-bar"
  style="background:{{ section.settings.bg_color }};color:{{ section.settings.text_color }};">
  <p class="announcement-bar__text">{{ section.settings.text }}</p>
  {%- if section.settings.dismissable -%}
    <button aria-label="Dismiss" onclick="
      this.parentElement.style.display='none';
      sessionStorage.setItem('ann-dismissed','1');">&times;</button>
  {%- endif -%}
</div>
<script>
  if (sessionStorage.getItem('ann-dismissed') === '1') {
    document.getElementById('announcement-bar').style.display = 'none';
  }
</script>
{%- endif -%}

sessionStorage keeps dismissal sticky for the session, then resets on next visit. Add a {% schema %} block for theme editor controls.

2. Countdown timer (replaces Bold Countdown, $19/mo)

{% comment %} snippets/countdown-timer.liquid {% endcomment %}
<div class="countdown" data-end="{{ end_date }}">
  <span data-days>00</span>d <span data-hours>00</span>h
  <span data-minutes>00</span>m <span data-seconds>00</span>s
</div>
<script>
(function(){
  var el = document.querySelector('.countdown[data-end]');
  if(!el) return;
  var end = new Date(el.dataset.end).getTime();
  function tick(){
    var diff = Math.max(0, end - Date.now());
    el.querySelector('[data-days]').textContent = String(Math.floor(diff/86400000)).padStart(2,'0');
    el.querySelector('[data-hours]').textContent = String(Math.floor((diff%86400000)/3600000)).padStart(2,'0');
    el.querySelector('[data-minutes]').textContent = String(Math.floor((diff%3600000)/60000)).padStart(2,'0');
    el.querySelector('[data-seconds]').textContent = String(Math.floor((diff%60000)/1000)).padStart(2,'0');
    if(diff > 0) requestAnimationFrame(tick);
  }
  tick();
})();
</script>

Call with {% render 'countdown-timer', end_date: '2026-04-15T23:59:59' %}. Vanilla JS, under 600 bytes minified.

3. Trust badges (replaces Smart Trust Badges, $4.99/mo)

{% comment %} snippets/trust-badges.liquid {% endcomment %}
<div class="trust-strip">
  <div class="trust-strip__item">
    <svg width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor"
      stroke-width="2"><path d="M12 22s8-4 8-10V5l-8-3-8 3v7c0 6 8 10 8 10z"/></svg>
    <span>Secure Checkout</span>
  </div>
  <div class="trust-strip__item">
    <svg width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor"
      stroke-width="2"><polyline points="23 4 23 10 17 10"/></svg>
    <span>30-Day Returns</span>
  </div>
</div>

Inline SVG beats raster sprites from app bundles. Render below your add-to-cart with {% render 'trust-badges' %}.

4. Size chart from metafields (replaces Kiwi Size Chart, $6.99/mo)

{% comment %} snippets/size-chart.liquid {% endcomment %}
{%- if product.metafields.custom.size_chart != blank -%}
  {%- assign chart = product.metafields.custom.size_chart.value -%}
  <button onclick="document.getElementById('size-chart-modal').showModal()">Size Guide</button>
  <dialog id="size-chart-modal">
    <button onclick="this.closest('dialog').close()" aria-label="Close">&times;</button>
    <table>
      <thead><tr>{%- for h in chart.headers -%}<th>{{ h }}</th>{%- endfor -%}</tr></thead>
      <tbody>
        {%- for row in chart.rows -%}
          <tr>{%- for cell in row -%}<td>{{ cell }}</td>{%- endfor -%}</tr>
        {%- endfor -%}
      </tbody>
    </table>
  </dialog>
{%- endif -%}

Native HTML <dialog> element, zero JS library. Set up the JSON metafield once, edit per product. See the metafields reference.

5. Product tabs (replaces EasyTabs, $5/mo)

{% comment %} snippets/product-tabs.liquid {% endcomment %}
<div class="product-tabs">
  <div role="tablist">
    <button class="is-active" data-tab="description" aria-selected="true">Description</button>
    <button data-tab="shipping" aria-selected="false">Shipping</button>
  </div>
  <div class="panel is-active" data-panel="description">{{ product.description }}</div>
  <div class="panel" data-panel="shipping">
    <p>Free shipping over {{ 7500 | money }}. Delivery 3 to 5 business days.</p>
  </div>
</div>
<script>
document.querySelectorAll('.product-tabs button').forEach(function(b){
  b.addEventListener('click', function(){
    var t = this.dataset.tab, root = this.closest('.product-tabs');
    root.querySelectorAll('button,.panel').forEach(function(x){x.classList.remove('is-active');});
    this.classList.add('is-active');
    root.querySelector('[data-panel="'+t+'"]').classList.add('is-active');
  });
});
</script>

ARIA attributes shipped. 487 bytes of JS replaces a 73KB app bundle.

6. Free shipping progress bar (replaces Hextom FSB, $9.99/mo)

{% comment %} snippets/free-shipping-bar.liquid {% endcomment %}
{%- assign threshold = 7500 -%}
{%- assign remaining = threshold | minus: cart.total_price -%}
{%- assign progress = cart.total_price | times: 100 | divided_by: threshold -%}
{%- if progress > 100 -%}{%- assign progress = 100 -%}{%- endif -%}
<div class="shipping-progress">
  <div class="bar"><div class="fill" style="width:{{ progress }}%"></div></div>
  {%- if remaining > 0 -%}
    <p>Add <strong>{{ remaining | money }}</strong> for free shipping</p>
  {%- else -%}
    <p>You qualify for free shipping</p>
  {%- endif -%}
</div>

I add this to nearly every cart drawer I build. On Mobelglede.no, swapping the Hextom bar for this snippet lifted AOV 11% over 14 days. The | money filter handles every currency Shopify supports. Never hardcode $.

7. Honest stock indicator (replaces Sticky Tools, $9.99/mo)

{% comment %} snippets/stock-indicator.liquid {% endcomment %}
{%- if variant.inventory_management == 'shopify' and variant.inventory_policy == 'deny' -%}
  {%- if variant.inventory_quantity <= 0 -%}
    <p class="stock stock--out">Sold out</p>
  {%- elsif variant.inventory_quantity <= 5 -%}
    <p class="stock stock--low">Only {{ variant.inventory_quantity }} left</p>
  {%- elsif variant.inventory_quantity <= 20 -%}
    <p class="stock stock--ok">In stock, ships within 24 hours</p>
  {%- else -%}
    <p class="stock stock--ok">In stock</p>
  {%- endif -%}
{%- endif -%}

Fake urgency erodes trust. This snippet only fires when stock is genuinely low. Same principle drives every CRO audit I run.

8. Recently viewed products (replaces LimeSpot, $18/mo)

{% comment %} snippets/recently-viewed.liquid {% endcomment %}
<div id="recently-viewed" style="display:none;">
  <h3>Recently Viewed</h3>
  <div id="rv-grid"></div>
</div>
<script>
(function(){
  var KEY = 'recently_viewed', current = '{{ product.handle }}';
  var items = JSON.parse(localStorage.getItem(KEY) || '[]')
    .filter(function(h){ return h !== current; });
  if(current){ items.unshift(current); items = items.slice(0, 6);
    localStorage.setItem(KEY, JSON.stringify(items)); }
  var handles = items.filter(function(h){ return h !== current; }).slice(0, 4);
  var grid = document.getElementById('rv-grid');
  handles.forEach(function(h){
    fetch('/products/' + h + '.js').then(function(r){return r.json();}).then(function(p){
      grid.insertAdjacentHTML('beforeend',
        '<a href="/products/'+p.handle+'"><img src="'+p.featured_image+'&width=300" loading="lazy" width="150" height="150" alt="'+p.title+'"><br>'+p.title+'</a>');
      document.getElementById('recently-viewed').style.display = '';
    });
  });
})();
</script>

localStorage plus the Shopify Product JSON endpoint. Runs only on PDP, not every page like an app.

9. Cross-sell using Shopify’s recommendations API (replaces Rebuy, $99/mo)

{% comment %} snippets/product-recommendations.liquid {% endcomment %}
<div id="rec" data-url="{{ routes.product_recommendations_url }}?section_id=product-recommendations&product_id={{ product.id }}&limit=4"></div>
<script>
(function(){
  var el = document.getElementById('rec');
  if(!el) return;
  fetch(el.dataset.url).then(function(r){return r.text();}).then(function(html){
    var t = document.createElement('div'); t.innerHTML = html;
    var inner = t.querySelector('.rec__inner');
    if(inner && inner.children.length) el.innerHTML = inner.outerHTML;
  });
})();
</script>
<div class="rec__inner">
  {%- if recommendations.performed and recommendations.products.size > 0 -%}
    <h3>You may also like</h3>
    {%- for p in recommendations.products -%}
      <a href="{{ p.url }}">{{ p.featured_image | image_url: width: 300 | image_tag: loading: 'lazy', alt: p.title }}
        <span>{{ p.title }} {{ p.price | money }}</span></a>
    {%- endfor -%}
  {%- endif -%}
</div>

Same AI engine the apps wrap. No app fee, no extra script, no telemetry pinging a vendor.

10. Cart upsell by product tag (replaces ReConvert, $14.99/mo)

{% comment %} snippets/cart-upsell.liquid {% endcomment %}
{%- assign upsell = nil -%}
{%- for item in cart.items -%}
  {%- for tag in item.product.tags -%}
    {%- if tag contains 'upsell:' -%}
      {%- assign handle = tag | remove: 'upsell:' -%}
      {%- assign in_cart = false -%}
      {%- for ci in cart.items -%}
        {%- if ci.product.handle == handle -%}{%- assign in_cart = true -%}{%- endif -%}
      {%- endfor -%}
      {%- unless in_cart -%}{%- assign upsell = all_products[handle] -%}{%- endunless -%}
    {%- endif -%}
  {%- endfor -%}
{%- endfor -%}
{%- if upsell and upsell.available -%}
  <div class="cart-upsell">Frequently bought together: {{ upsell.title }} {{ upsell.price | money }}</div>
{%- endif -%}

Tag products with upsell:other-handle. Powers the upsell stack on Factory Direct Blinds, where it lifted attach rate from 4% to 11%.

Factory Direct Blinds homepage with the Liquid powered upsell stack that replaced multiple paid apps

11. Responsive images with srcset (replaces Crush.pics, $6/mo)

{% comment %} snippets/responsive-image.liquid {% endcomment %}
{%- if image -%}
  {{ image | image_url: width: 800 | image_tag:
    loading: eager | default: 'lazy',
    widths: '200,400,600,800,1000',
    sizes: '(max-width: 768px) 100vw, 50vw',
    alt: alt | default: '' }}
{%- endif -%}

The image_tag filter generates a proper srcset. Browser downloads only the size the viewport needs. Never use raw CDN URLs. Always pipe through image_url with a width.

12. Critical CSS inline + deferred stylesheet (replaces Page Speed Booster, $14.99/mo)

{% comment %} layout/theme.liquid <head> {% endcomment %}
{%- if template contains 'index' -%}
<style>
  .hero { min-height: 80vh; display: flex; align-items: center; }
  .hero h1 { font-size: clamp(2rem, 5vw, 3.5rem); font-weight: 800; }
  .hero .btn { padding: 1rem 2rem; border-radius: 12px; }
</style>
{%- endif -%}
<link rel="stylesheet" href="{{ 'theme.css' | asset_url }}" media="print" onload="this.media='all'">
<noscript><link rel="stylesheet" href="{{ 'theme.css' | asset_url }}"></noscript>

The media="print" onload="this.media='all'" swap defers non-critical CSS without blocking render. This pattern took Factory Direct Blinds from PageSpeed 38 to 81 mobile.

Factory Direct Blinds PageSpeed Insights mobile report showing all green Core Web Vitals

13. Product JSON-LD schema (replaces JSON-LD for SEO, $4.99/mo)

{% comment %} snippets/product-schema.liquid {% endcomment %}
<script type="application/ld+json">
{
  "@context": "https://schema.org",
  "@type": "Product",
  "name": {{ product.title | json }},
  "description": {{ product.description | strip_html | truncate: 500 | json }},
  "url": "{{ shop.url }}{{ product.url }}",
  "image": [{%- for i in product.images limit: 5 -%}"{{ i | image_url: width: 1200 }}"{% unless forloop.last %},{% endunless %}{%- endfor -%}],
  "brand": { "@type": "Brand", "name": {{ product.vendor | json }} },
  "sku": {{ product.selected_or_first_available_variant.sku | json }},
  "offers": {
    "@type": "AggregateOffer",
    "priceCurrency": "{{ shop.currency }}",
    "lowPrice": {{ product.price_min | divided_by: 100.0 }},
    "highPrice": {{ product.price_max | divided_by: 100.0 }},
    "availability": "https://schema.org/{% if product.available %}InStock{% else %}OutOfStock{% endif %}"
  }
}
</script>

Complete Product schema for rich Google results. Conditional aggregateRating block goes here when reviews are present.

14. Auto table of contents (replaces TOC apps)

{% comment %} snippets/table-of-contents.liquid {% endcomment %}
{%- assign headings = content | split: '<h2' -%}
{%- if headings.size > 2 -%}
<nav class="toc"><p class="toc__title">Contents</p><ol>
  {%- for block in headings -%}
    {%- if forloop.first -%}{%- continue -%}{%- endif -%}
    {%- assign text = block | split: '>' | last | split: '</h2>' | first | strip_html | strip -%}
    {%- assign id_parts = block | split: 'id="' -%}
    {%- if id_parts.size > 1 -%}
      {%- assign id = id_parts[1] | split: '"' | first -%}
    {%- else -%}
      {%- assign id = text | handleize -%}
    {%- endif -%}
    {%- if text != blank -%}<li><a href="#{{ id }}">{{ text }}</a></li>{%- endif -%}
  {%- endfor -%}
</ol></nav>
{%- endif -%}

Parses H2s out of article.content and links them. Works with any auto-generated heading IDs.

15. Custom contact form (replaces Hulk Form Builder, $9.90/mo)

{% comment %} sections/custom-contact-form.liquid {% endcomment %}
{% form 'contact', class: 'custom-form' %}
  {%- if form.posted_successfully? -%}
    <p>Thanks. We will reply within 24 hours.</p>
  {%- endif -%}
  {%- if form.errors -%}<div>{{ form.errors | default_errors }}</div>{%- endif -%}
  <input type="text" name="contact[name]" placeholder="Your name" required>
  <input type="email" name="contact[email]" placeholder="[email protected]" required>
  {%- for block in section.blocks -%}
    <div {{ block.shopify_attributes }}>
      <label>{{ block.settings.label }}</label>
      {%- if block.type == 'textarea' -%}
        <textarea name="contact[{{ block.settings.label }}]" rows="4"></textarea>
      {%- else -%}
        <input type="text" name="contact[{{ block.settings.label }}]">
      {%- endif -%}
    </div>
  {%- endfor -%}
  <button type="submit">{{ section.settings.button_text }}</button>
{% endform %}

Submissions land in Shopify’s native notification system (Settings > Notifications). Add a {% schema %} block with text_field, textarea, and select block types so merchants can build the form in the theme editor.

How to verify in 5 minutes

  1. Run PageSpeed Insights on a product URL before and after. Mobile score should move 5 to 25 points.
  2. Open DevTools > Network, filter by JS, sort by size. Confirm the app’s bundle is gone.
  3. Click through the replaced UI on iPhone Safari and Chrome desktop. Test the dismissable bar, the size chart modal, the cart upsell add.
  4. Keep the app installed but disabled for 7 days as a rollback path. Then uninstall.

The takeaway

  • Audit your installed apps this week. Most stores carry 5 to 8 frontend-only apps that 200 lines of Liquid replace.
  • Replace before you uninstall. Build, test on a duplicate theme, ship, monitor 7 days, then remove.
  • Always pipe prices through | money. Hardcoded currency symbols break international stores.
  • Test on mobile Safari first. Most app conflicts surface there before Chrome desktop.
  • Pocket the savings as profit, not as more apps. A clean theme is the highest-impact CRO asset you own.

For the full Liquid architecture pattern that ties these snippets together, read my Shopify Liquid development guide. For more performance work, see my Liquid loop optimization guide.

Want me to audit your app stack?

I find $200 to $500/month in killable apps on every store I audit. The replacement code loads faster, costs nothing to maintain, and gives you full control. View my services or book a free strategy call.

Frequently Asked Questions

Will removing apps break my store?

Not if you replace the functionality before deactivating the app. The process is: build the custom Liquid replacement, test it on a duplicate theme, verify it works across devices, then uninstall the app. Never remove an app without confirming the replacement is live and working. I recommend keeping the app installed but disabled for a week after switching, so you can quickly revert if needed.

How do I know which apps can be replaced with Liquid?

Apps that primarily add frontend UI elements are the best candidates: announcement bars, trust badges, countdown timers, size charts, product tabs, free shipping bars, and stock level indicators. Apps that handle backend logic - email marketing, inventory syncing, subscription management, tax calculation - generally cannot be replaced with Liquid because they require server-side processing and API integrations.

Can these snippets work with any Shopify theme?

Yes, with minor adjustments. The Liquid code is universal across all Shopify themes. You may need to adjust CSS class names, color variables, and container widths to match your specific theme's design system. Section-based snippets work on any Online Store 2.0 theme. For older vintage themes, you may need to use snippets with include tags instead of sections.

Do I need a developer to implement these?

Basic snippets like trust badges and announcement bars can be implemented by anyone comfortable editing Shopify theme files. More complex snippets like cart upsells, recently viewed products, and structured data require understanding of Liquid logic and JavaScript. If you are not confident editing theme code, hire a developer - a botched implementation can break your checkout or product pages.

How much can I save by replacing apps with Liquid code?

The average Shopify store spends $200-500 per month on apps that could be replaced with custom Liquid code. A typical replacement project costs $500-2,000 as a one-time fee, meaning you break even within 1-4 months and save $2,400-6,000 per year going forward. Beyond cost savings, you also gain 1-3 seconds of page speed improvement from removing app JavaScript, which directly impacts conversion rates.

Book Strategy Call