> ## Documentation Index
> Fetch the complete documentation index at: https://docs.stockful.app/llms.txt
> Use this file to discover all available pages before exploring further.

# Location Availability

> Show per-location stock on product pages

Location availability lets customers check which stores or warehouses have a product in stock. This is especially useful for businesses with retail locations where customers might want to buy in-person or choose click-and-collect.

## No-code setup

The simplest approach is to use the built-in **Stockful Location Availability** theme block:

1. In the theme editor, navigate to your product template
2. Click **Add block > Apps > Stockful Location Availability**
3. Configure the button text, style, and maximum locations to show

This adds a button that opens a slide-out drawer with live stock data. See the [theme blocks guide](/storefront/guides/theme-blocks) for details on settings.

## Custom implementation

For full control over the UI, use the `window.stockful` JavaScript API.

### Basic location list

```html theme={null}
<button id="check-stores" type="button">Check store availability</button>
<div id="store-list" style="display: none; margin-top: 1em;"></div>

<template id="store-row">
  <div style="display: flex; justify-content: space-between; padding: 0.6em 0; border-bottom: 1px solid #eee;">
    <div>
      <div data-name style="font-weight: 500;"></div>
      <div data-city style="font-size: 0.85em; opacity: 0.6;"></div>
    </div>
    <div data-status></div>
  </div>
</template>

<script>
  const btn = document.getElementById("check-stores");
  const container = document.getElementById("store-list");
  const template = document.getElementById("store-row");

  btn.addEventListener("click", async () => {
    const variantId = new URLSearchParams(window.location.search).get("variant");
    if (!variantId || !window.stockful) return;

    container.style.display = "block";
    container.replaceChildren(Object.assign(document.createElement("p"), { textContent: "Loading..." }));

    const locations = await window.stockful.getLocationAvailability(Number(variantId));

    if (locations.length === 0) {
      container.replaceChildren(Object.assign(document.createElement("p"), { textContent: "No store data available." }));
      return;
    }

    const fragment = document.createDocumentFragment();

    for (const loc of locations) {
      const row = template.content.cloneNode(true);
      const inStock = loc.available > 0 || loc.status === "in_stock";

      row.querySelector("[data-name]").textContent = loc.name;

      const city = row.querySelector("[data-city]");
      if (loc.city) {
        city.textContent = loc.city;
      } else {
        city.remove();
      }

      const status = row.querySelector("[data-status]");
      status.style.color = inStock ? "#16a34a" : "#dc2626";
      status.textContent = loc.available != null
        ? (inStock ? `${loc.available} available` : "Out of stock")
        : (inStock ? "In stock" : "Out of stock");

      fragment.appendChild(row);
    }

    container.replaceChildren(fragment);
  });
</script>
```

### Modal approach

If you prefer a modal over an inline list, combine the location data with a Shopify `ui-modal` or your own modal component:

```html theme={null}
<button id="open-locations" type="button">Check store availability</button>

<template id="modal-row">
  <div style="display: flex; justify-content: space-between; padding: 0.5em 0; border-bottom: 1px solid #f0f0f0;">
    <span data-name></span>
    <span data-status></span>
  </div>
</template>

<dialog id="locations-modal" style="
  border: none;
  border-radius: 12px;
  padding: 1.5em;
  max-width: 450px;
  width: 90vw;
  box-shadow: 0 10px 40px rgba(0,0,0,0.15);
">
  <div style="display: flex; justify-content: space-between; align-items: center; margin-bottom: 1em;">
    <h3 style="margin: 0;">Store Availability</h3>
    <button id="close-locations" type="button" style="background: none; border: none; font-size: 1.2em; cursor: pointer;">&times;</button>
  </div>
  <div id="modal-locations"></div>
</dialog>

<script>
  const modal = document.getElementById("locations-modal");
  const content = document.getElementById("modal-locations");
  const template = document.getElementById("modal-row");

  document.getElementById("open-locations").addEventListener("click", async () => {
    modal.showModal();
    content.replaceChildren(Object.assign(document.createElement("p"), { textContent: "Loading..." }));

    const id = new URLSearchParams(window.location.search).get("variant");
    if (!id || !window.stockful) return;

    const locations = await window.stockful.getLocationAvailability(Number(id));
    if (locations.length === 0) {
      content.replaceChildren(Object.assign(document.createElement("p"), { textContent: "No store data available." }));
      return;
    }

    const fragment = document.createDocumentFragment();

    for (const loc of locations) {
      const row = template.content.cloneNode(true);
      const inStock = loc.available > 0 || loc.status === "in_stock";

      row.querySelector("[data-name]").textContent = loc.name;

      const status = row.querySelector("[data-status]");
      status.style.color = inStock ? "#16a34a" : "#dc2626";
      status.textContent = loc.available != null
        ? (inStock ? `${loc.available} available` : "Out of stock")
        : (inStock ? "In stock" : "Out of stock");

      fragment.appendChild(row);
    }

    content.replaceChildren(fragment);
  });

  document.getElementById("close-locations").addEventListener("click", () => modal.close());
</script>
```

## Controlling visibility

**Visible locations** are controlled in the Stockful app under **Settings > Storefront** - choose which locations appear to customers (e.g. hide your main warehouse, show only retail stores). This applies to both the built-in theme block and the `window.stockful` API.

**Show available quantity** is a per-block setting on the theme block. When enabled, customers see "42 available"; when disabled, they see "In stock" / "Out of stock". For custom implementations using the JS API, you control this in your own code.

## Tips

* **Retail stores only** - most merchants hide warehouses and fulfillment centers, showing only locations where customers can visit
* **Quantities vs. status** - showing exact quantities can be useful for high-demand items but may seem odd for regular products. Status labels ("In stock" / "Out of stock") are a safer default
* **Live data** - location availability is fetched live from Shopify (not from cached metafields), so it's always up to date. The trade-off is a slightly longer load time compared to metafield-based data
* **Cache duration** - location data is cached for 60 seconds (vs 30 seconds for variant/product data), so very recent inventory changes may take up to a minute to appear
