Postcode search

Improving the user experience for the majority of users, adding a postcode search that returns AAA accessible results.

The problem

Users needed to find and select a UK address. The existing approach relied on manual entry: address line one, line two, town, county, postcode. It worked, but it was slow. For users who knew their postcode, it was friction they didn’t need. Research showed that the majority of users would prefer to enter a postcode and choose from a list.

The question was how to do that without introducing new problems. Address lookup services exist, but many implementations fail on accessibility. Autocomplete components that don’t announce options to screen readers. Results that can’t be navigated with keyboard. ARIA patterns that look right but behave wrong. We needed a solution that was both useful and AAA accessible.

The approach

We integrated a postcode lookup that returns addresses from a reliable data source. The implementation had to meet three criteria:

Functional. Return accurate results for valid UK postcodes. Handle partial postcodes. Surface clear errors when no results were found.

Accessible. Full keyboard navigation. Proper ARIA roles and labels. Screen reader announcements for results loading, selection, and errors. Focus management when the list appears and when an option is chosen.

Progressive enhancement. The form still works without JavaScript. Users can enter an address manually if the lookup fails, or if they prefer. The postcode search is an enhancement, not a requirement.

The result

Users who know their postcode can now find their address in two steps: enter postcode, select from list. The interaction is faster and reduces typo-induced validation errors. For the majority of users, it’s a meaningful improvement.

The accessibility audit confirmed AAA compliance. The component was tested with screen readers, keyboard-only navigation, and various assistive technology combinations. The result is a postcode search that works for everyone, not just the majority.

How you can do this for your service

To achieve the required functionality we used Axios for API callbacks and the OS Places API. First you need to install the API by simply running:

npm install axios

In your terminal, in the root folder of your prototype. Then you need to call the API at the top of your routes.js file:

const express = require('express');
const router = express.Router();
const path = require('node:path');

// API
const axios = require('axios');

// Add your routes here - above the module.exports line

And here is the code that controls all three pages (the key is obfuscated for security):

router.post('/find-subject-uk-address', function (req, res) {
  var postcodeLookup = req.session.data['postcode'];

  const regex = RegExp(
    '^(([gG][iI][rR] {0,}0[aA]{2})|((([a-pr-uwyzA-PR-UWYZ][a-hk-yA-HK-Y]?[0-9][0-9]?)|(([a-pr-uwyzA-PR-UWYZ][0-9][a-hjkstuwA-HJKSTUW])|([a-pr-uwyzA-PR-UWYZ][a-hk-yA-HK-Y][0-9][abehmnprv-yABEHMNPRV-Y]))) {0,}[0-9][abd-hjlnp-uw-zABD-HJLNP-UW-Z]{2}))$'
  );

  if (postcodeLookup) {
    if (regex.test(postcodeLookup) === true) {
      axios
        .get(
          'https://api.os.uk/search/places/v1/postcode?postcode=' +
            postcodeLookup +
            '&key=' +
            'XXXXXXXXXXXXXXXXXXXXXXXX'
        )
        .then((response) => {
          var addresses = response.data.results.map((result) => result.DPA.ADDRESS);

          const titleCaseAddresses = addresses.map((address) => {
            const parts = address.split(', ');
            const formattedParts = parts.map((part, index) => {
              if (index === parts.length - 1) {
                // Preserve postcode (SW1 3AF) in uppercase
                return part.toUpperCase();
              }
              return part
                .split(' ')
                .map((word) => word.charAt(0).toUpperCase() + word.slice(1).toLowerCase())
                .join(' ');
            });
            return formattedParts.join(', ');
          });

          req.session.data['addresses'] = titleCaseAddresses;

          res.redirect('select-subject-uk-address');
        })
        .catch((error) => {
          console.log(error);
          res.redirect('/discretionary/subject-uk-address');
        });
    }
  } else {
    res.redirect('/find-subject-uk-address');
  }
});

Conclusion

The introduction of the postcode search functionality was a direct response to user feedback and a testament to our commitment to user-centered design. This implementation not only streamlines the address input process but also enhances the overall user experience, making our service meet the users’ needs.