# Address autocomplete in Shopware 6

> Load the one-tag address script from a Shopware 6 theme so every address form gets autocomplete, with postcode and city filled in the right form.
>
> https://danadresse.dk/en/guides/shopware · verified 2026-11-02
Shopware 6 uses the same address form for registration, in checkout and under "My addresses". The fields are named `<prefix>[street]`, `<prefix>[zipcode]` and `<prefix>[city]`, where the prefix is e.g. `billingAddress`, `shippingAddress` or `address`. Our one-tag script `danadresse-auto` knows these names and fills postcode and city with the same prefix, so shipping and billing addresses do not get mixed up.

## 1. Create a browser key

Create a *live* key under [Keys](/dashboard/keys) and lock it to the shop's domains under **Allowed domains**, including your staging domain. If you have several sales channels with their own domains, all of them must be on the list. See [Browser keys and allowed domains](/en/guides/browser-noegler-domaener).

## 2. Load the script in the storefront

Extend `base.html.twig` in your theme, e.g. `src/Resources/views/storefront/base.html.twig`, and insert your browser key:

```twig id=auto-twig run=no
{% sw_extends '@Storefront/storefront/base.html.twig' %}

{% block base_body_script %}
    {{ parent() }}
    <script src="https://api.danadresse.dk/js/danadresse-auto.min.js"
            data-key="YOUR_BROWSER_KEY" defer></script>
{% endblock %}
```

Clear the cache with `bin/console cache:clear`. An extension that adds header or footer scripts works too.

The script recognises `billingAddress[street]`, `shippingAddress[street]` and `address[street]` with their `[zipcode]` and `[city]`. The names were checked against Shopware's own storefront templates. It also picks up forms that appear later, such as the address editor that checkout loads into a modal. When the customer picks a suggestion, street and number (with floor and door), postcode and city are filled. A typed address is checked when the customer leaves the field, with a "Did you mean …?" suggestion.

## 3. Test

Test three places: registration as a new customer, the address selection in checkout, and **Account → Addresses**. Type an address, pick a suggestion, and check that postcode and city land in the same form. `API refused the request (403): … locked to …` in the console means the domain is missing from the key. If your server sends a `Content-Security-Policy` header, `https://api.danadresse.dk` must be in both `script-src` and `connect-src`.

If your theme uses other field names, or you have a checkout plugin, add `data-debug` to the tag and read in the console which fields were found. Point the script at the fields with `data-fields`, e.g. `data-fields='[{"street":"#my-street","zip":"#my-zip","city":"#my-city"}]'`.

## Or: your own init script

If you want full control over what is filled, attach the address widget yourself. Put the file in your theme or plugin bundle as `src/Resources/public/danadresse-init.js`:

```js id=init-js run=syntax
const API_KEY = "YOUR_BROWSER_KEY";

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 }));
  }
}

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;
}

// billingAddress[street] → billingAddress[zipcode] / billingAddress[city]
function sibling(input, field) {
  const name = input.name.replace("[street]", "[" + field + "]");
  return (input.form || document).querySelector('[name="' + name + '"]');
}

function bindAll() {
  document.querySelectorAll('input[name$="[street]"]').forEach((input) => {
    DanadresseAutocomplete.attach(input, {
      apiKey: API_KEY,
      onSelect(a) {
        setField(input, streetLine(a), ["change"]);
        setField(sibling(input, "zipcode"), a.postnr);
        setField(sibling(input, "city"), a.postnrnavn);
      },
    });
  });
}

function start() {
  bindAll();
  // The address editor in checkout is loaded with AJAX into a modal
  new MutationObserver(bindAll).observe(document.body, { childList: true, subtree: true });
}

if (document.readyState === "loading") document.addEventListener("DOMContentLoaded", start);
else start();
```

Load it from `base.html.twig` in place of the script tag from step 2:

```twig id=base-twig run=no
{% sw_extends '@Storefront/storefront/base.html.twig' %}

{% block base_body_script %}
    {{ parent() }}
    <script src="https://danadresse.dk/static/js/danadresse-autocomplete-1.0.0.js" defer></script>
    <script src="{{ asset('bundles/danadressetheme/danadresse-init.js') }}" defer></script>
{% endblock %}
```

`bin/console assets:install` copies `src/Resources/public/` to `public/bundles/<bundle name in lowercase>/`. Adjust the path in `asset()` to match your bundle's name. Then clear the cache. With this route `https://danadresse.dk` must be in `script-src` too.

## Optional: check the address on the server

A subscriber on `CheckoutOrderPlacedEvent` can send street, number and postcode to `/datavask/adgangsadresser?betegnelse=…` with a server key without a domain lock, and flag the order if the answer is category C. See [Address cleansing in checkout](/en/guides/datavask-i-checkout) for the categories.

## Next steps

- [Magento 2](/en/guides/magento), if you are comparing with another PHP platform
- [Browser keys and allowed domains](/en/guides/browser-noegler-domaener)
- [Integrations](/en/integrationer)
