MonkeyLoader All articles
Performance

Postcode First, Pixels Later: A Store Locator That Doesn't Drag the Map Along

MonkeyLoader
Postcode First, Pixels Later: A Store Locator That Doesn't Drag the Map Along

A store locator has one job: help someone find the nearest branch without making them wait. Yet plenty of UK retail sites greet visitors by loading a mapping SDK, a tile layer, a marker-cluster plugin and a geocoder before anyone has typed a single character. On a train out of Reading or a farm track in Devon, that is a lot of dead weight for a question that a list could answer.

The better pattern is almost boring: postcode first, list second, map much later. The map is not the price of admission. It is a feature you earn by showing intent.

The map is not the job

When a shopper lands on a store locator, they usually want three things: the closest shop, whether it is open, and how to get there. A clean list of results does most of that work. The map confirms the shape of the journey, but it is not the first thing they need.

So stop treating the map as the page. Treat it as a tool. The initial page should be a postcode form, a short explainer and maybe a few popular stores. That is it. No tiles, no SDK, no marker images, no 'locate me' prompt fighting for permission.

Search by postcode, not by map drag

A postcode-first search is small, quick and very UK-friendly. The visitor types something like SW1A 1AA or M1 1AE, and your backend does the lookup. You can geocode with a service such as postcodes.io, or use coordinates you already hold for each branch. Either way, the browser does not need a map library to ask the question.

Your endpoint should return a short JSON payload: branch name, address, distance, opening hours, latitude, longitude and a static map URL. Five nearest stores is usually enough. If the postcode is invalid, say so plainly and let them try again. No modal, no spinner theatre.

Render that list as normal HTML. Use a form so the thing still works if JavaScript has a bad day. Add a directions link for each branch. At this stage, nobody has loaded a single map tile.

A loader with one job

The map loader should be tiny and explicit. Put a Show map button on each result or on the results panel. When someone clicks it, then you import the heavy module. A dynamic import keeps the SDK out of the initial bundle and gives you a clear place to handle failure.

const buttons = document.querySelectorAll('[data-map]');

buttons.forEach((btn) => {
  btn.addEventListener('click', async () => {
    btn.disabled = true;
    btn.textContent = 'Loading map…';

    try {
      const { showMap } = await import('/assets/map-loader.js');
      await showMap(btn.dataset.map, btn.dataset.lat, btn.dataset.lon);
      btn.hidden = true;
    } catch (err) {
      btn.textContent = 'Map unavailable — use the directions link';
    }
  });
});

That is the whole trick. The SDK lives behind one interaction. If the visitor never asks for a map, they never pay for it. If they do ask, they have already told you they want it.

The static map earns its keep

A static map image is a useful middle step. It is one image, not a live map: no panning, no zooming, no tile requests. At around 600 pixels wide in WebP or a well-compressed JPEG, it gives a spatial clue without the weight of an interactive map.

Lazy-load it below the fold. Give it honest alt text that names the branch and street. Link it to a full directions page or a native maps app. If the image fails, the address and directions link still do the job. That is the fallback that matters.

Do not hide every result behind the static map, though. The list is the primary content. The image is a helpful extra, not a gate.

Data Saver, low signal and polite defaults

UK mobile signal is not a uniform thing. A shopper in central London may have five bars; someone in rural Cornwall may be clinging to one. Data Saver on Android Chrome and Low Data Mode on iOS exist for good reason.

Check navigator.connection.saveData where it is available. If it is true, skip the static map and make the interactive map a deliberate choice, not an automatic load. If effectiveType is slow-2g or 2g, do the same. Be honest about the limits: iOS Low Data Mode is not reliably exposed to the browser, so do not pretend you can detect it. Give people a clear button and keep the default light.

A few other small things help: cache recent postcode results in sessionStorage for the tab, use system fonts instead of loading a display face for the locator, and put a sensible timeout on the postcode lookup with AbortController. A four-second timeout with a plain error message beats a spinner that never ends.

Measure the win

Do not just assert that it feels faster. Measure the initial page without the map SDK and compare it with the old behaviour. Look at transferred JavaScript, total bytes before interaction, Largest Contentful Paint and main-thread work on a mid-range Android phone. A full mapping SDK can add hundreds of kilobytes of script before tiles even enter the picture. Deferring it changes the shape of the page load.

Track two events: map_button_clicked and map_loaded. That tells you how many people actually want the interactive map. Add the connection type as a dimension if your analytics allows it. You may find that most visitors never click, which is not a failure. It means the list did its job.

Set a budget you can defend. For example: locator-specific JavaScript under 30KB gzipped on first load, excluding your shared site bundle. No map SDK in the critical path. Static map lazy-loaded and skipped when Data Saver is on. Then keep the budget in CI so it does not quietly drift.

What to ship

The build is not complicated. A postcode form. A small endpoint that returns the nearest branches. A server-rendered list with addresses and directions links. A static map image somewhere sensible. A Show map button wired to a dynamic import. A saveData check. A timeout. A couple of analytics events. That is a store locator that respects the visitor's connection and their attention.

Maps are lovely when you need them. They are a terrible first impression when all someone wanted was the postcode of the shop nearest the station. Postcode first, pixels later.

All Articles

Related Articles

Leave the Diary Blank Until They Click: A Booking Widget That Earns Its Place

Leave the Diary Blank Until They Click: A Booking Widget That Earns Its Place

Assume the Signal Will Drop: A Loader Built for the Morning Commute

Assume the Signal Will Drop: A Loader Built for the Morning Commute

The Font That Waits Its Turn: Public-Sector Type Without the Jolt

The Font That Waits Its Turn: Public-Sector Type Without the Jolt