Shopify Store Locator Generator

A location list with a click-to-switch map, no Google Maps API key needed.

Describe the result you need. AI will set up the tool and ask for any missing details.

Configure with AI

Enter to submit · Shift + Enter for a new line

Sent to AI. Leave out passwords and private customer data.

Your details

Locations

Name | Address | Phone | Hours, one store per line. Phone and hours are optional. Up to 30 locations.

Map
Style

Your section file (sections/store-locator.liquid)

store-locator.liquid
{%- comment -%}
  Store locator. Add one Location block per store in the theme editor; each gets a "Get
  directions" link (Google Maps, no API key needed) and, if the map is on, clicking a location
  swaps a plain Maps embed to that address.

  Managing many stores as a metaobject instead? Swap the {% for block in section.blocks %} loop
  below for {% for location in metaobjects.store_location.values %} and read
  location.name.value, location.address.value etc. once you've created a "Store location"
  metaobject definition under Settings → Custom data → Metaobjects.
{%- endcomment -%}
{%- style -%}
  #StoreLocator-{{ section.id }}{background:#ffffff;padding:1.5rem 1rem}
  #StoreLocator-{{ section.id }} .ea-sl__heading{margin:0 0 1rem;font-size:1.3em;font-weight:600}
  #StoreLocator-{{ section.id }} .ea-sl__layout{display:grid;grid-template-columns:minmax(240px,340px) 1fr;gap:1.5rem;align-items:start}
  #StoreLocator-{{ section.id }} .ea-sl__list{list-style:none;margin:0;padding:0;display:flex;flex-direction:column;gap:.5rem;max-height:420px;overflow:auto}
  #StoreLocator-{{ section.id }} .ea-sl__item{border:1px solid #e5e5e5;border-radius:8px;padding:.75rem .9rem}
  #StoreLocator-{{ section.id }} .ea-sl__item.is-active{border-color:#1a1a1a}
  #StoreLocator-{{ section.id }} .ea-sl__select{display:block;width:100%;text-align:left;background:none;border:0;padding:0;font:inherit;cursor:pointer;color:inherit}
  #StoreLocator-{{ section.id }} .ea-sl__name{display:block;font-weight:600}
  #StoreLocator-{{ section.id }} .ea-sl__address,#StoreLocator-{{ section.id }} .ea-sl__hours{display:block;font-size:.9em;opacity:.75;margin-top:.15rem}
  #StoreLocator-{{ section.id }} .ea-sl__links{display:flex;gap:.75rem;margin-top:.5rem;font-size:.85em}
  #StoreLocator-{{ section.id }} .ea-sl__links a{color:#1a1a1a}
  #StoreLocator-{{ section.id }} .ea-sl__map{position:relative;min-height:320px;border-radius:8px;overflow:hidden;background:#eee}
  #StoreLocator-{{ section.id }} .ea-sl__map iframe{position:absolute;inset:0;width:100%;height:100%;border:0}
  @media (max-width:640px){#StoreLocator-{{ section.id }} .ea-sl__layout{grid-template-columns:1fr}}
{%- endstyle -%}

<div id="StoreLocator-{{ section.id }}" class="ea-sl">
  {%- if section.settings.heading != blank -%}
    <p class="ea-sl__heading">{{ section.settings.heading | escape }}</p>
  {%- endif -%}
  <div class="ea-sl__layout">
    <ul class="ea-sl__list" role="list">
      {%- for block in section.blocks -%}
        <li class="ea-sl__item{% if forloop.first %} is-active{% endif %}" {{ block.shopify_attributes }}>
          <button type="button" class="ea-sl__select" data-address="{{ block.settings.address | escape }}" {% if forloop.first %}aria-current="true"{% endif %}>
            <span class="ea-sl__name">{{ block.settings.name | escape }}</span>
            <span class="ea-sl__address">{{ block.settings.address | escape }}</span>
            {%- if block.settings.hours != blank -%}<span class="ea-sl__hours">{{ block.settings.hours | newline_to_br }}</span>{%- endif -%}
          </button>
          <div class="ea-sl__links">
            {%- if block.settings.phone != blank -%}
              <a href="tel:{{ block.settings.phone | remove: ' ' | remove: '(' | remove: ')' | remove: '-' }}">{{ block.settings.phone }}</a>
            {%- endif -%}
            <a href="https://www.google.com/maps/dir/?api=1&destination={{ block.settings.address | url_encode }}" target="_blank" rel="noopener">Get directions</a>
          </div>
        </li>
      {%- endfor -%}
    </ul>
    {%- if section.settings.show_map and section.blocks.size > 0 -%}
      <div class="ea-sl__map">
        <iframe
          id="StoreLocatorMap-{{ section.id }}"
          title="Store map"
          loading="lazy"
          referrerpolicy="no-referrer-when-downgrade"
          src="https://maps.google.com/maps?q={{ section.blocks.first.settings.address | url_encode }}&z={{ section.settings.map_zoom }}&output=embed"
        ></iframe>
      </div>
    {%- endif -%}
  </div>
</div>

{%- if section.settings.show_map -%}
<script>
  (function () {
    var root = document.getElementById('StoreLocator-{{ section.id }}');
    var frame = document.getElementById('StoreLocatorMap-{{ section.id }}');
    if (!root || !frame) return;
    var buttons = root.querySelectorAll('[data-address]');
    for (var i = 0; i < buttons.length; i++) {
      buttons[i].addEventListener('click', function (e) {
        var btn = e.currentTarget;
        var items = root.querySelectorAll('.ea-sl__item');
        for (var j = 0; j < items.length; j++) { items[j].classList.remove('is-active'); }
        btn.closest('.ea-sl__item').classList.add('is-active');
        frame.src = 'https://maps.google.com/maps?q=' + encodeURIComponent(btn.getAttribute('data-address')) + '&z={{ section.settings.map_zoom }}&output=embed';
      });
    }
  })();
</script>
{%- endif -%}

{% schema %}
{
  "name": "Store locator",
  "tag": "section",
  "class": "ea-store-locator-section",
  "max_blocks": 30,
  "settings": [
    {
      "type": "text",
      "id": "heading",
      "label": "Heading",
      "default": "Find a store near you"
    },
    {
      "type": "checkbox",
      "id": "show_map",
      "label": "Show a map",
      "default": true
    },
    {
      "type": "range",
      "id": "map_zoom",
      "label": "Map zoom",
      "min": 10,
      "max": 18,
      "step": 1,
      "default": 14,
      "info": "Higher is closer."
    },
    {
      "type": "header",
      "content": "Colors"
    },
    {
      "type": "color",
      "id": "accent",
      "label": "Accent",
      "default": "#1a1a1a"
    },
    {
      "type": "color",
      "id": "background",
      "label": "Background",
      "default": "#ffffff"
    }
  ],
  "blocks": [
    {
      "type": "location",
      "name": "Location",
      "settings": [
        {
          "type": "text",
          "id": "name",
          "label": "Name"
        },
        {
          "type": "text",
          "id": "address",
          "label": "Address",
          "info": "Used for the map and the directions link."
        },
        {
          "type": "text",
          "id": "phone",
          "label": "Phone (optional)"
        },
        {
          "type": "textarea",
          "id": "hours",
          "label": "Hours (optional)"
        }
      ]
    }
  ],
  "presets": [
    {
      "name": "Store locator",
      "blocks": [
        {
          "type": "location",
          "settings": {
            "name": "Downtown Flagship",
            "address": "123 Main St, Austin, TX 78701",
            "phone": "(512) 555-0142",
            "hours": "Mon–Sat 10am–7pm, Sun 11am–5pm"
          }
        },
        {
          "type": "location",
          "settings": {
            "name": "Westside Outlet",
            "address": "4500 Bee Cave Rd, Austin, TX 78746",
            "phone": "(512) 555-0198",
            "hours": "Mon–Sun 10am–8pm"
          }
        }
      ]
    }
  ]
}
{% endschema %}

To install: in Shopify admin go to Online Store → Themes, click … next to your theme → Edit code. Under Sections choose Add a new section, name it as shown above, replace the contents with this code and save. Then open Customize and add the section where you want it. Try it on a duplicate of your theme first. Add one Location block per store, or use the "Store locator" preset already filled in from what you entered here.

The map is a plain Google Maps embed keyed to the address text, not a geocoded pin, so keep addresses formatted normally (street, city, state) for Maps to find them reliably.

Live preview

No API key required

Most store locator tutorials assume the Google Maps JavaScript API, which needs a Google Cloud account, billing enabled, and an API key you have to keep secret. This section skips all of that by using a plain Google Maps embed URL (maps.google.com/maps?q=...&output=embed), which loads inside an iframe with no key and no account.

Each store is its own block in the theme editor, so staff can add or edit locations without touching code. Clicking a location in the list swaps the embedded map to that address and adds a "Get directions" link built from Google's directions URL format, which also works without a key.

  • If you manage locations as a metaobject instead of blocks, swap the block loop for {% for location in metaobjects.store_location.values %}, once you've created a matching metaobject definition under Settings → Custom data → Metaobjects.
  • The map is a plain address lookup, not a geocoded pin, so keep addresses in a normal street/city/state format.

Adding it to your theme

  1. 01

    Add the section

    Online Store → Themes → Edit code → Sections → Add a new section named store-locator, paste the code, then add it to a page (like a Store Locator or Contact page) in Customize. A Location block preset is already filled in from what you entered here.

Questions people ask

Do I need a Google Maps API key?

No. This uses a plain Maps embed URL, which works without an API key or a Google Cloud account for basic display and directions links.

Can I show all locations on one map with pins?

Not with this generator. A single map with a pin per store needs the Google Maps JavaScript API (a key, billing and more setup). This gives you one embed that switches address when you click a different location, which covers most small chains without that overhead.

How many locations can I add?

Up to 30 blocks. For more, consider the metaobjects approach mentioned in the guide, which scales better in the theme editor.

Will this work if I don't use Online Store 2.0?

It's written as a section for Online Store 2.0 themes (sections with blocks, edited in Customize). Vintage themes without section support would need a developer to adapt it.

Want this builtinto your store?

I'm Emmanuel, a Shopify and WooCommerce developer. If this tool pointed at something you'd rather have fixed for you (a faster store, cleaner product data, a migration, a custom feature), send me a note.

Emmanuel Asika

Emmanuel Asika

Shopify & WooCommerce developer, Dublin

I build and fix online stores: new builds, migrations, speed, SEO and the custom bits apps can't do.

hello@emmanuelasika.com

Last reviewed 30 September 2026.

Sources: Google Maps Embed without an API key (output=embed)