# Address autocomplete in Shopify — where it works and where it does not

> Add the address widget to your Shopify theme, fill postcode and city automatically, and see exactly where Shopify allows scripts.
>
> https://danadresse.dk/en/guides/shopify · verified 2026-11-02
Shopify lets you run your own JavaScript in your theme, but not in the checkout itself. This guide shows where the address widget can run today, how to attach it to the address fields, and what you cannot reach without Shopify Plus.

## Where the widget can run

| Place | Works today? |
|---|---|
| Theme pages: contact form, your own fields on product or cart pages | Yes |
| The address book under "My account" with classic customer accounts (`customers/addresses`) | Yes |
| The new customer accounts, hosted by Shopify | No, the theme cannot script them |
| Checkout (shipping and billing) | No, Shopify does not run theme scripts in checkout |

For checkout, Shopify has extension targets for address autocomplete (`purchase.address-autocomplete.suggest`). They need an app with protected customer data access, and checkout UI extensions are Shopify Plus only. Our Shopify app (checkout autocomplete on Plus, and an address check of every new order that tags the order and suggests a correction) is built but not available to install yet. Until it is listed on [Integrations](/en/integrationer), use the theme route below. Checkout also has Shopify's own address autocomplete for a list of countries.

If you build headless with Hydrogen, you control the form yourself. See the [Shopify page](/en/shopify-adresse) and the [React guide](/en/guides/react-autocomplete).

## 1. Create a browser key

The key sits in the page source, so anyone can read it. Lock it to your domains: create a *live* key under [Keys](/dashboard/keys) and enter the domains under **Allowed domains**, e.g. `myshop.dk`, `www.myshop.dk` and `myshop.myshopify.com`.

Enter the `myshopify.com` domain exactly. A wildcard such as `*.myshopify.com` covers every Shopify shop. More in [Browser keys and allowed domains](/en/guides/browser-noegler-domaener).

## The quickest route: one script tag

Open `layout/theme.liquid` under **Online Store → Themes → … → Edit code** and paste this just before `</body>`:

```liquid id=auto-tag run=no
<script src="https://api.danadresse.dk/js/danadresse-auto.min.js"
        data-key="YOUR_BROWSER_KEY" defer></script>
```

The script finds the address forms of the classic customer accounts (`address[address1]`, `address[zip]`, `address[city]`) by itself and fills street, postcode and city. For fields with other names, such as a contact form, add `data-fields`, e.g. `data-fields='[{"street":"#ContactForm-address","zip":"#ContactForm-zip","city":"#ContactForm-city"}]'`. Add `data-debug` to see in the browser console what it found. If you prefer to control the code yourself, use steps 2 and 3 instead.

## 2. Or: add your own init file to the theme

Go to **Online Store → Themes → … → Edit code**. Create the file `assets/danadresse-init.js` with the code below and insert your key.

```js id=init-js run=syntax
// assets/danadresse-init.js
const API_KEY = "YOUR_BROWSER_KEY"; // locked to your domains

// Sets the value so that theme scripts and frameworks notice the change too.
function setField(el, value, events) {
  if (!el || value == null) return;
  const setter = Object.getOwnPropertyDescriptor(Object.getPrototypeOf(el), "value").set;
  setter.call(el, value);
  for (const type of events || ["input", "change"]) {
    el.dispatchEvent(new Event(type, { bubbles: true }));
  }
}

// "Rådhuspladsen 1, st. th" — street, number, floor and door on one line.
function streetLine(a) {
  let line = a.vejnavn + " " + a.husnr;
  if (a.etage) line += ", " + a.etage + ".";
  if (a["dør"]) line += " " + a["dør"];
  return line;
}

function bind(input) {
  const form = input.form || document;
  DanadresseAutocomplete.attach(input, {
    apiKey: API_KEY,
    onSelect(a) {
      setField(input, streetLine(a), ["change"]);
      setField(form.querySelector('[name="address[zip]"]'), a.postnr);
      setField(form.querySelector('[name="address[city]"]'), a.postnrnavn);
    },
  });
}

document.querySelectorAll('input[name="address[address1]"]').forEach(bind);
```

The address book has one form for a new address and one per existing address. That is why the code attaches to every field named `address[address1]` and fills postcode and city in the same form.

## 3. Load the scripts in theme.liquid

Open `layout/theme.liquid` and paste the two lines just before `</body>`. Both use `defer`, so they run in order once the page has loaded.

```liquid id=theme-liquid run=no
<script src="https://danadresse.dk/static/js/danadresse-autocomplete-1.0.0.js" defer></script>
<script src="{{ 'danadresse-init.js' | asset_url }}" defer></script>
```

Do not use the `script_tag` filter here. It writes a script without `defer`, which then runs before the widget has loaded.

## 4. Other fields and testing

The names above are the ones Shopify's `customer_address` form uses in standard themes. Your theme may differ: right-click the field, choose **Inspect**, and read its `name` or `id`. In a contact form the fields are called `contact[…]`, e.g. `contact[Address]`.

Test in a private window: type "rådhuspladsen 1 slagelse", pick a suggestion, and check that postcode and city are filled. If the console shows `403 origin_not_allowed`, the domain is missing from the key.

Shopify does not send orders through your theme, so server-side validation has to happen in an app or workflow that reads the order. See [Address cleansing in checkout](/en/guides/datavask-i-checkout).

## Next steps

- [Browser keys and allowed domains](/en/guides/browser-noegler-domaener)
- [All widget options](/en/widget)
- [Integrations](/en/integrationer)
