Local & Weather

Free Store Directions Button Widget

One-click Google Maps directions to your business, with travel-mode picker.

A free button that opens Google Maps directions straight to your address, with an optional driving, walking, or transit selector. Built for local businesses, restaurants, and shops that want fewer "where are you?" phone calls. Add one script tag with your address and it works anywhere.

Live demo

Embed this widget

Paste either snippet into your page — a Custom HTML block on WordPress, Shopify, or Squarespace works too. Both snippets update live as you customize, so what you copy is exactly what you configured.

Hosted (recommended)html
<script src="https://hayneslab.dev/widgets/store-directions-button.js" data-hl-address="200 S Tryon St, Charlotte, NC 28202" async></script>
<!-- Free Store Directions Button widget by haynes lab — https://hayneslab.dev/tools/widgets/store-directions-button -->
Inline (self-contained)html
<!-- Free Store Directions Button widget by haynes lab — https://hayneslab.dev/tools/widgets/store-directions-button -->
<script data-hl-address="200 S Tryon St, Charlotte, NC 28202">
/*!
 * haynes lab — Store Directions Button widget (free)
 * Docs & embed code: https://hayneslab.dev/tools/widgets/store-directions-button
 */
(function () {
  'use strict';

  var PREFIX = 'hlw-store-directions-button';
  var STYLE_ID = 'hlw-store-directions-button-styles';
  var DIRECTIONS_URL = 'https://www.google.com/maps/dir/?api=1';
  var MODES = [
    { value: 'driving', label: 'Driving' },
    { value: 'walking', label: 'Walking' },
    { value: 'transit', label: 'Transit' }
  ];

  function findScript() {
    return document.currentScript ||
      document.querySelector('script[src*="widgets/store-directions-button.js"]');
  }

  function injectStyles() {
    if (document.getElementById(STYLE_ID)) return;
    var css =
      '.' + PREFIX + ', .' + PREFIX + ' * { box-sizing: border-box; }' +
      '.' + PREFIX + ' { display: inline-flex; flex-wrap: wrap; align-items: stretch; gap: 8px;' +
      ' font-family: system-ui, -apple-system, "Segoe UI", Roboto, Helvetica, Arial, sans-serif;' +
      ' font-size: 16px; line-height: 1.4; }' +
      '.' + PREFIX + '__button { display: inline-flex; align-items: center; gap: 8px;' +
      ' padding: 12px 22px; border: 0; border-radius: 8px; background: var(--hlw-bg); color: #ffffff;' +
      ' font-size: 15px; font-weight: 600; font-family: inherit; text-decoration: none;' +
      ' cursor: pointer; transition: filter 0.15s ease; }' +
      '.' + PREFIX + '__button:hover { filter: brightness(0.92); }' +
      '.' + PREFIX + '__button:focus-visible { outline: 2px solid var(--hlw-bg); outline-offset: 2px; }' +
      '.' + PREFIX + '__button svg { display: block; flex: 0 0 auto; }' +
      '.' + PREFIX + '__mode { padding: 12px 10px; border: 1px solid #d1d5db; border-radius: 8px;' +
      ' background: #ffffff; color: #374151; font-size: 14px; font-family: inherit; cursor: pointer; }' +
      '.' + PREFIX + '__mode:focus-visible { outline: 2px solid var(--hlw-bg); outline-offset: 2px; }' +
      '.' + PREFIX + '__wrap { display: inline-flex; flex-direction: column; align-items: flex-start; gap: 4px; }' +
      '.' + PREFIX + '__credit { font-size: 11px; display: inline-flex; align-items: center; gap: 4px;' +
      ' color: #9ca3af; text-decoration: none; }' +
      '.' + PREFIX + '__credit svg { display: block; }' +
      '.' + PREFIX + '__credit:hover { color: #6b7280; text-decoration: underline; }' +
      '@media (max-width: 480px) { .' + PREFIX + ' { width: 100%; }' +
      ' .' + PREFIX + '__button { flex: 1 1 auto; justify-content: center; } }';
    var style = document.createElement('style');
    style.id = STYLE_ID;
    style.textContent = css;
    document.head.appendChild(style);
  }

  function el(tag, className, text) {
    var node = document.createElement(tag);
    if (className) node.className = className;
    if (text != null) node.textContent = text;
    return node;
  }

  function logoSvg() {
    var svgNS = 'http://www.w3.org/2000/svg';
    var svg = document.createElementNS(svgNS, 'svg');
    svg.setAttribute('viewBox', '0 0 32 32');
    svg.setAttribute('width', '13');
    svg.setAttribute('height', '13');
    svg.setAttribute('aria-hidden', 'true');
    svg.setAttribute('focusable', 'false');
    function circle(cx, cy, r, attrs) {
      var c = document.createElementNS(svgNS, 'circle');
      c.setAttribute('cx', cx);
      c.setAttribute('cy', cy);
      c.setAttribute('r', r);
      for (var key in attrs) c.setAttribute(key, attrs[key]);
      svg.appendChild(c);
    }
    circle(16, 16, 16, { fill: '#0f0a0a' });
    circle(16, 16, 12, { fill: 'none', stroke: '#fff5f5', 'stroke-width': 1, opacity: 0.3 });
    circle(16, 16, 9, { fill: 'none', stroke: '#fff5f5', 'stroke-width': 1, opacity: 0.5 });
    circle(16, 16, 6, { fill: 'none', stroke: '#fff5f5', 'stroke-width': 1.2, opacity: 0.9 });
    circle(16, 16, 3, { fill: '#06b6d4' });
    circle(24.5, 7.5, 2, { fill: '#f97316' });
    circle(7, 16, 1.8, { fill: '#f97316' });
    circle(20, 20, 1.5, { fill: '#f97316' });
    return svg;
  }

  function pinSvg() {
    var svgNS = 'http://www.w3.org/2000/svg';
    var svg = document.createElementNS(svgNS, 'svg');
    svg.setAttribute('viewBox', '0 0 24 24');
    svg.setAttribute('width', '16');
    svg.setAttribute('height', '16');
    svg.setAttribute('aria-hidden', 'true');
    svg.setAttribute('focusable', 'false');
    svg.setAttribute('fill', 'none');
    svg.setAttribute('stroke', 'currentColor');
    svg.setAttribute('stroke-width', '2');
    svg.setAttribute('stroke-linecap', 'round');
    svg.setAttribute('stroke-linejoin', 'round');
    var path = document.createElementNS(svgNS, 'path');
    path.setAttribute('d', 'M20 10c0 6-8 12-8 12s-8-6-8-12a8 8 0 0 1 16 0Z');
    svg.appendChild(path);
    var dot = document.createElementNS(svgNS, 'circle');
    dot.setAttribute('cx', '12');
    dot.setAttribute('cy', '10');
    dot.setAttribute('r', '3');
    svg.appendChild(dot);
    return svg;
  }

  function build(script) {
    var address = (script.getAttribute('data-hl-address') || '').trim();
    if (!address) {
      console.warn('[haynes lab store-directions-button] Missing required data-hl-address attribute. Nothing rendered.');
      return;
    }
    var label = (script.getAttribute('data-hl-label') || '').trim() || 'Get Directions';
    var bg = script.getAttribute('data-hl-bg') || '#f97316';

    injectStyles();

    var wrap = el('div', PREFIX + '__wrap');
    var root = el('div', PREFIX);
    root.style.setProperty('--hlw-bg', bg);

    var button = el('a', PREFIX + '__button');
    button.target = '_blank';
    button.rel = 'noopener';
    button.appendChild(pinSvg());
    button.appendChild(document.createTextNode(label));

    var mode = el('select', PREFIX + '__mode');
    mode.setAttribute('aria-label', 'Travel mode');
    MODES.forEach(function (m) {
      var opt = el('option', null, m.label);
      opt.value = m.value;
      mode.appendChild(opt);
    });

    function update() {
      button.href = DIRECTIONS_URL +
        '&destination=' + encodeURIComponent(address) +
        '&travelmode=' + encodeURIComponent(mode.value);
    }
    mode.addEventListener('change', update);
    update();

    root.appendChild(button);
    root.appendChild(mode);
    wrap.appendChild(root);

    var credit = el('a', PREFIX + '__credit');
    credit.appendChild(logoSvg());
    credit.appendChild(document.createTextNode('Powered by haynes lab'));
    credit.href = 'https://hayneslab.dev/tools/widgets/store-directions-button';
    credit.target = '_blank';
    credit.rel = 'noopener';
    wrap.appendChild(credit);

    if (script.parentNode && script.parentNode.nodeName !== 'HEAD') {
      script.insertAdjacentElement('afterend', wrap);
    } else {
      document.body.appendChild(wrap);
    }
  }

  function init() {
    var script = findScript();
    if (!script) {
      console.warn('[haynes lab store-directions-button] Could not locate its own script tag. Nothing rendered.');
      return;
    }
    build(script);
  }

  if (document.body) {
    init();
  } else {
    document.addEventListener('DOMContentLoaded', init);
  }
})();

</script>

Configuration

Every option is an attribute on the script tag — the same attributes the customizer above exposes. All attributes are optional unless marked required — defaults apply when omitted.

AttributeDefaultDescription
data-hl-address(required)The street address or place name visitors get directions to (URL-encoded automatically).
data-hl-label"Get Directions"Text shown on the directions button.
data-hl-bg"#f97316"Background color of the directions button.

Frequently asked questions

How do I install the Store Directions Button?

Paste the script tag where you want the button, set data-hl-address to your street address, and the button renders right there. Clicking it opens Google Maps directions in a new tab.

Can I change the button text, color, or travel mode?

Yes — data-hl-label sets the button text and data-hl-bg sets its background color. Visitors pick driving, walking, or transit from the built-in select before clicking.

Does it work on WordPress, Shopify, or Squarespace?

Yes — it is a single vanilla JS file with no dependencies. Paste the snippet into any HTML block, embed section, or theme template and it works.

More free widgets

Need a custom widget?

We build custom widgets, integrations, and whole products. Tell us what your site needs and we'll scope it.

Get in Touch