Dutch address autocomplete with Alpine.js

by Jeffrey van Rossum

In the Netherlands, it's common for forms with address fields to try and autocomplete the street and city based on the zipcode and house number.

Using Alpine.js

In order to achieve this with Alpine, we would have to trigger the fetching of the data when the user has filled in the zipcode and house number field. Then, the result should be set on the address and city fields. We can do this with the @input event listener.

We will listen on both fields and then execute handle the lookup in a separate function called getAddress.

<div x-data="{street: '', zipcode: '', city: '', number: ''}">
  <div>
    <label>Zipcode</label>
    <input type="text" x-model="zipcode" @input.debounce.100ms="getAddress($data, $data.zipcode, $data.number)">
  </div>
  
  <div>
    <label>House number</label>
    <input type="text" x-model="number" @input.debounce.100ms="getAddress($data, $data.zipcode, $data.number)">
  </div>
  
  <div>
    <label>Street</label>
    <input type="text" x-model="street">
  </div>
  
  <div>
    <label>City</label>
    <input type="text" x-model="city">
  </div>
</div>

The function gets three values. One holds the Alpine data which we need to later modify the data with the results fetch from the API. The other two are the values from the zipcode and the house number fields.

Address API

There are several commercial API's that provide you with a way to look up address information, but there is a free API from the government as well.

In this example, we will be using the free API from Nationaal Georegister.

The getAddress function

The function to fetch the address is async, because we need to wait for the API response. We also do some basics checks, because we don't want to send requests when the fields are incomplete. We also make sure the zipcode contains 6 characters. This all could be further polished up.

async function getAddress($data, _zipcode, number) {
  let zipcode = _zipcode.replace(/\s/g, '');

  if(zipcode.length < 6 || number.length === 0) {
    return;
  }

  let response = await fetch('https://api.pdok.nl/bzk/locatieserver/search/v3_1/free?fq=postcode:'+zipcode.replace(/\s/g, '')+'&fq=huisnummer~'+number+'*');
    
  let data = await response.json();

  if(data.response.numFound === 0) {
    $data.street = null;
    $data.city = null;

    return;
  }

  $data.street = data.response.docs[0].straatnaam;
  $data.city = data.response.docs[0].woonplaatsnaam;
}

Demo

This post was last modified 31 March 2024

Comments

Talk about this article on X.
Did you like this post?

If you sign up for my newsletter, I can keep you up to date on more posts like this when they are published.