Shopify Location-Based Product Visibility Using Product Metafields
Overview
While RP-Promap Adds the ability to use Location Targeting , this guide explains how to configure a Shopify store so customers only see products available at their selected location.
The implementation uses:
Shopify product metafields
A customer location selector
Liquid rendering
JavaScript filtering
Search integration
Optional add-to-cart restriction
This method works without Shopify Plus and does not require a custom app.
Requirements
Before starting, ensure your Shopify store has:
Online Store access
Theme code editing access
Product metafields enabled
A theme that supports custom Liquid edits (Dawn recommended)
System Architecture
Customer selects location ↓ Location saved in browser storage ↓ Collections + Search + Product pages filter products ↓ Only products assigned to that location remain visible
Step 1 — Create Product Metafield
Open:
Shopify Admin → Settings → Custom Data → Products
Create a metafield:
Setting | Value |
Name | Available Locations |
Namespace and key |
|
Type | List → Single line text |
Step 2 — Assign Locations to Products
Open a product.
Under metafields:
Available Locations
Add values such as:
miami orlando dallas
Example:
Product | Locations |
Miami Hoodie | miami |
Florida Tee | miami, orlando |
National Product | miami, orlando, dallas |
Step 3 — Create Location Selector Snippet
Create:
snippets/location-selector.liquid
Add:
<div class="location-selector"> <label for="locationSelect">Choose location:</label> <select id="locationSelect"> <option value="">All locations</option> <option value="miami">Miami</option> <option value="orlando">Orlando</option> <option value="dallas">Dallas</option> </select> </div> <script> document.addEventListener('DOMContentLoaded', function () { const select = document.getElementById('locationSelect'); const saved = localStorage.getItem('selected_location') || ''; select.value = saved; select.addEventListener('change', function () { localStorage.setItem('selected_location', this.value); document.dispatchEvent(new CustomEvent('locationChanged', { detail: { location: this.value } })); filterProductsByLocation(); }); }); </script>Step 4 — Add Selector to Header
Open:
sections/header.liquid
Add:
{% render 'location-selector' %}Recommended placement:
Top navigation
Header utility area
Announcement bar
Step 5 — Configure Collection Filtering
Open your collection grid section.
Usually:
sections/main-collection-product-grid.liquid
Find:
{% for product in collection.products %}Replace product wrapper with:
{% for product in collection.products %} {% assign locations = product.metafields.custom.available_locations.value %} <div class="location-filter-product" data-product-locations="{% if locations != blank %}{{ locations | join: ',' | escape }}{% endif %}" > {% render 'card-product', card_product: product %} </div> {% endfor %}Step 6 — Configure Search Filtering
Open:
sections/main-search.liquid
Find:
{% for item in search.results %}Update product rendering:
{% for item in search.results %} {% if item.object_type == 'product' %} {% assign locations = item.metafields.custom.available_locations.value %} <div class="location-filter-product" data-product-locations="{% if locations != blank %}{{ locations | join: ',' | escape }}{% endif %}" > {% render 'card-product', card_product: item %} </div> {% else %} <div class="search-result-item"> <!-- existing non-product result code --> </div> {% endif %} {% endfor %}Step 7 — Add Global Filtering JavaScript
Open:
layout/theme.liquid
Before:
</body>
Add:
<script> function filterProductsByLocation() { const selectedLocation = localStorage.getItem('selected_location') || ''; const products = document.querySelectorAll('.location-filter-product'); products.forEach(function(product) { const locations = product.dataset.productLocations || ''; const locationList = locations.split(',').map(function(location) { return location.trim(); }); if (!selectedLocation || locationList.includes(selectedLocation)) { product.style.display = ''; } else { product.style.display = 'none'; } }); } document.addEventListener('DOMContentLoaded', filterProductsByLocation); document.addEventListener('locationChanged', filterProductsByLocation); document.addEventListener('shopify:section:load', function () { filterProductsByLocation(); }); </script>Step 8 — Product Page Availability Message
Open your product template section.
Usually:
sections/main-product.liquid
Add:
{% assign locations = product.metafields.custom.available_locations.value %} <div id="locationProductStatus" data-product-locations="{% if locations != blank %}{{ locations | join: ',' | escape }}{% endif %}" ></div> <script> document.addEventListener('DOMContentLoaded', function () { const box = document.getElementById('locationProductStatus'); if (!box) return; const selectedLocation = localStorage.getItem('selected_location') || ''; const locations = box.dataset.productLocations || ''; const locationList = locations.split(',').map(function(location) { return location.trim(); }); if (!selectedLocation) { box.innerHTML = 'Select a location to check availability.'; } else if (locationList.includes(selectedLocation)) { box.innerHTML = 'Available at your selected location.'; } else { box.innerHTML = 'Not available at your selected location.'; } }); </script>Step 9 — Optional Add-to-Cart Restriction
Prevent checkout for unavailable locations.
Add below the previous script:
<script> document.addEventListener('DOMContentLoaded', function () { const selectedLocation = localStorage.getItem('selected_location') || ''; const statusBox = document.getElementById('locationProductStatus'); const addButton = document.querySelector('button[name="add"]'); if (!statusBox || !addButton || !selectedLocation) return; const locations = statusBox.dataset.productLocations || ''; const locationList = locations.split(',').map(function(location) { return location.trim(); }); if (!locationList.includes(selectedLocation)) { addButton.disabled = true; addButton.textContent = 'Unavailable at selected location'; } }); </script>Recommended Enhancements
Geolocation Auto-Selection
Automatically select the nearest store using:
Shopify Geolocation API
Cloudflare country headers
IP geolocation services
Store Pickup Integration
Combine with:
Shopify Local Pickup
Store Pickup apps
Delivery radius apps
Dynamic Inventory Sync
Advanced implementations can:
Sync actual Shopify inventory levels
Use Storefront API
Show per-location stock quantities
Limitations
This implementation:
✅ Filters storefront visibility
✅ Filters search results
✅ Supports collections
✅ Supports metafields
✅ Supports product pages
This implementation does NOT:
❌ Change Shopify inventory routing
❌ Change fulfillment logic
❌ Restrict warehouse assignment
❌ Modify Shopify checkout inventory behavior
Recommended Use Cases
Ideal for:
Multi-store retailers
Regional inventory
Franchise systems
Warehouse segmentation
Localized catalogs
Pickup-based commerce
Troubleshooting
Products Still Visible
Verify:
Product metafield values are correct
JavaScript loaded successfully
Product wrapper contains
data-product-locations
Dropdown Not Saving
Check:
Browser localStorage enabled
No JavaScript console errors
Add-to-Cart Still Enabled
Verify:
Product page script added
Theme uses
button[name="add"]
Some themes use custom selectors.
RP Promap Notes
Recommended naming convention:
custom.available_locations
Recommended location slugs:
miami orlando dallas
Use lowercase values only for consistency.
© Rose Perl Technology
