Skip to main content
When safety stock is enabled in Charlie, your theme needs to display sellable inventory instead of native Shopify availability. This guide shows how to integrate Charlie’s inventory metafields into any Shopify theme.

Reference implementation

See a complete working example in our reference theme repository based on Shopify’s Horizon theme.

Who this is for

  • Theme developers customizing themes for Charlie users
  • Agencies building or modifying Shopify themes
  • Merchants with custom themes who want accurate storefront availability

Namespace and metafields

Theme integration uses the app-reserved namespace app--{APP_ID}--inventory. The full list of keys, types, and how to find your app ID is in Metafields reference.
For non-theme integrations (product feeds, smart collections, storefront filters), use the public charlie_inventory namespace instead. See Metafields reference.

Implementation patterns

Basic setup

At the start of any file that needs safety stock awareness, define the namespace and check if safety stock is enabled.
{% liquid
  assign inventory_ns = 'app--YOUR_APP_ID--inventory'
  assign safety_enabled = shop.metafields[inventory_ns].safety_stock_enabled.value
%}
Replace YOUR_APP_ID with your actual app ID from the namespace you identified earlier.
We recommend checking safety_stock_enabled before applying overrides. This ensures your theme gracefully handles scenarios where Charlie is uninstalled or safety stock is disabled. Without this check, stale metafield values could cause incorrect availability displays.

Product availability

Use this pattern on collection pages and product cards to determine if a product should show as available.
{% liquid
  if safety_enabled
    assign charlie_meta = product.metafields[inventory_ns].available
    if charlie_meta != nil
      assign product_available = charlie_meta.value
    else
      assign product_available = product.available
    endif
  else
    assign product_available = product.available
  endif
%}

Variant availability

Use this pattern on product detail pages and variant pickers to determine if a specific variant is available.
{% liquid
  if safety_enabled
    assign charlie_meta = variant.metafields[inventory_ns].available
    if charlie_meta != nil
      assign variant_available = charlie_meta.value
    else
      assign variant_available = variant.available
    endif
  else
    assign variant_available = variant.available
  endif
%}

First available variant

Use this pattern when linking to products to ensure the URL points to a sellable variant.
{% liquid
  assign safety_first = nil
  if safety_enabled and product.metafields[inventory_ns].available.value
    assign safety_first = product.metafields[inventory_ns].first_available_variant.value
  endif
  assign selected_variant = product.selected_variant | default: safety_first | default: product.selected_or_first_available_variant
%}

Variant JSON for JavaScript

This pattern is essential for variant pickers to work correctly. JavaScript relies on the variant JSON to determine which variants can be added to cart. The JSON replacement handles both space and no-space formats that Shopify may output.
When themes output variant JSON for JavaScript consumption, the available property must reflect sellable inventory.
{% capture variant_json %}{{ variant | json }}{% endcapture %}

{% if safety_enabled %}
  {% assign charlie_meta = variant.metafields[inventory_ns].available %}
  {% if charlie_meta != nil %}
    {% assign variant_available = charlie_meta.value %}
  {% else %}
    {% assign variant_available = variant.available %}
  {% endif %}

  {% if variant_available == true or variant_available == 'true' %}
    {% assign variant_json = variant_json | replace: '"available":false', '"available":true' %}
    {% assign variant_json = variant_json | replace: '"available": false', '"available": true' %}
  {% else %}
    {% assign variant_json = variant_json | replace: '"available":true', '"available":false' %}
    {% assign variant_json = variant_json | replace: '"available": true', '"available": false' %}
  {% endif %}
{% endif %}

{{ variant_json }}
The condition checks for both boolean true and string 'true' because Liquid metafield values may be returned as strings in some contexts.

Sold Out badges

Use this pattern on product cards to correctly show Sold Out badges based on sellable inventory.
{% liquid
  assign charlie_meta = product.metafields[inventory_ns].available
  if charlie_meta != nil
    assign product_available = charlie_meta.value
  else
    assign product_available = product.available
  endif

  if product_available == false
    assign show_sold_out_badge = true
  endif
%}

{% if show_sold_out_badge %}
  <span class="badge badge--sold-out">Sold Out</span>
{% endif %}

Price display

When showing prices, use first_available_variant to display the price of a sellable variant rather than an unavailable one.
{% liquid
  assign safety_first = nil
  if product.metafields[inventory_ns].available.value
    assign safety_first = product.metafields[inventory_ns].first_available_variant.value
  endif
  assign display_variant = product.selected_variant | default: safety_first | default: product.selected_or_first_available_variant
%}

{{ display_variant.price | money }}

Files to modify

The specific files depend on your theme structure. Here are common files that typically need updates.
File patternWhat to change
Product card snippetsProduct availability for collection pages
Variant picker snippetsVariant availability and JSON override
Swatch componentsSwatch availability states
Buy buttonsAdd to cart button enabled/disabled state
Quick add modalsAvailability in modal variant selectors
Price snippetsSelected variant for price display
Badge/label snippetsSold Out badge visibility
Inventory displayStock status based on sellable quantity

What to ignore

Local pickup availability uses total stock at physical locations and is unaffected by safety stock reserves. No changes are needed for pickup location availability displays.

Testing checklist

1

Enable safety stock

In Charlie, enable safety stock with “Block orders” mode for the clearest testing.
2

Create a test rule

Create a rule with 100% reserve on a test product so it shows zero sellable inventory.
3

Verify collection pages

Check that the product shows “Sold out” on collection pages.
4

Test variant switching

On the product page, switch between variants and verify availability updates correctly.
5

Verify add to cart

Confirm the add to cart button is disabled for unavailable variants.
6

Disable and verify

Disable safety stock in Charlie and verify the theme reverts to native Shopify behavior.

Troubleshooting

The namespace in your theme code does not match your Charlie installation’s app ID. Double-check the namespace in Shopify Admin under Settings, then Custom data, then Metafields, then Variants.
The variant JSON replacement pattern may not be catching all formats. Ensure you are replacing both "available":false (no space) and "available": false (with space) as Shopify may output either format.
Your code may be missing the safety_stock_enabled check. All safety stock logic should be wrapped in a conditional that first checks if safety stock is enabled at the shop level.
Quick add modals often have separate variant picker implementations. Ensure both the main product page and quick add modal snippets include the safety stock override logic.
When Charlie is uninstalled, inventory metafields are deleted. If your theme only checks for != nil, it will correctly fall back to native Shopify behavior. However, if you cached or hardcoded values, clear your theme cache and verify the metafield checks are working.

Metafields reference

All Charlie metafields and namespaces

App integrations

Wire product feeds, search, and filter apps

Safety Stock

Configure safety stock rules and settings

Inventory Overview

How Charlie syncs inventory data
Last modified on May 11, 2026