Skip to main content

Addressly — Custom integration guide

This guide explains how to build custom storefront experiences on top of Addressly Core (addressly.js) and the Main Widget (addressly-widget.js).

Written by Vikash Jha

Prerequisites

  1. Precise Location app embed block (addressly-main.liquid) on body — loads core, modal markup, Google Maps, and window._addressly bootstrap data.

  2. Addressly Widget section (addressly-section.liquid) — optional but typical; renders the delivery widget and preciseLocationSettings.

  3. Google Maps API key configured in the theme editor.

Core and widget initialize from addressly-main.liquid:

window._addressly.core = new Addressly(); window._addressly.core.init();  window._addressly.mainWidget = new AddresslyWidget(); window._addressly.mainWidget.init();

Wait for core to be ready before relying on location state:

document.addEventListener("addressly:core-ready", (event) => {   
const { location, assignedStore, assignedZone } = event.detail;
// Safe to read core + storage-backed state
});

Global object: window._addressly

Populated by Liquid in addressly-main.liquid and extended at runtime.

Property

Description

core

Addressly instance after init

mainWidget

AddresslyWidget instance after init

inlineWidget

AddresslyInlineWidget if present

coreSettings

Main block settings (location strategy, zones, checkout rules)

preciseLocationSettings

Widget section settings (store hours UI, etc.)

storeLocations

Active store metaobjects

deliveryZones

Delivery zone metaobjects

customer

Logged-in customer + verified addresses

googleMapsAPIKey, mapId, primaryColor

Maps / theming

page

Shopify template name

isInApployApp

Uses device bridge for geolocation when true

refreshCart()

Refreshes cart from Shopify (from addressly-cart-helper.js)

originalCart

Last known cart snapshot


Addressly Core API

Access via window._addressly.core.

State (in memory)

Property

Description

CURRENT_LOCATION

Confirmed/saved location object (also in localStorage)

ASSIGNED_STORE

Store assigned to current location

ASSIGNED_ZONE

Delivery zone matched for current location

temporaryPlaceData

In-progress selection before confirm (search, map pin, etc.)

Prefer processLocationData() to set a new confirmed location — it validates, persists, assigns store/zone, updates cart, and fires addressly:location-updated.

const core = window._addressly.core;

// Programmatically set location (must include address + coordinates)
await core.processLocationData({
coordinates: {
latitude: 40.7128,
longitude: -74.006,
map_location: core.getGoogleMapsUrl(40.7128, -74.006),
},
address: {
address1: "123 Main St",
city: "New York",
province: "NY",
province_code: "NY",
zip: "10001",
country: "United States",
country_code: "US",
},
customer_verified: true, // required if location_verification_required is enabled
address_id: null, // optional, for saved customer addresses
address_label: null,
address_type: null,
});

// Read current state without parsing storage
const location = core.CURRENT_LOCATION;
const store = core.getAssignedStore(); // may differ from ASSIGNED_STORE (zone vs nearest logic)
const inZone = core.isLocationInDeliveryZone();
const confirmed = core.isLocationConfirmed(null, true); // strict = requires coords + address
const checkout = core.shouldDisableCheckout(); // { shouldDisable, reasons[] }

Location helpers

// Browser or Apploy device location (user gesture recommended)
const live = await core.getCurrentLocation();

// Reverse geocode coordinates → address object
const address = await core.reverseGeocode({ latitude, lng, map_location });

// Nearest store / zone for arbitrary coordinates
const store = core.getStoreForLocation({ coordinates, address });
const zone = core.getZoneForLocation({ coordinates, address });

// Temporary place (modal flow)
core.updateTemporaryPlaceData({ coordinates, address });
core.updateTemporaryPlaceAddress({ address1: "..." });
core.getTemporaryPlaceData();
core.clearTemporaryPlaceData();

// Custom events (same bus as built-in events)
core.dispatchEvent("my-app:custom", { foo: "bar" });

Core settings (coreSettings)

Notable keys from the Precise Location block:

Setting

Values / effect

location_strategy

auto-detect, prompt-user, widget-triggered

location_verification_required

Requires customer_verified: true on saved location

location_fallback_to_ip

IP geolocation when auto-detect fails

far_from_location_warning

Fires addressly:far-from-location on homepage

out_of_zone_action

unrestricted, restricted, checkout_disabled

out_of_zone_message

Copy for out-of-zone modal

disable_checkout

never, location_not_provided, location_not_verified

checkout_button_selectors

CSS selectors for checkout CTAs


Main Widget API

Access via window._addressly.mainWidget.

Open / close modals

const widget = window._addressly.mainWidget;

// Modal types: "location" | "store-hours" | "out-of-delivery-zone" | "far-from-location"
widget.openAddresslyModal("location", 1); // step 1 = search / use my location
widget.openAddresslyModal("location", 2); // step 2 = map confirm
widget.openAddresslyModal("store-hours");
widget.openAddresslyModal("out-of-delivery-zone");
widget.openAddresslyModal("far-from-location");

widget.closeAddresslyModal(); // closes backdrop + resets location modal temp state
widget.resetAndCloseAddresslyModal(); // reset temp place + close

widget.isAddresslyModalOpen(); // backdrop has class "open"
widget.goBack(); // location modal: previous step or close on step 1

Location modal steps (data-step on .addressly-modal[data-modal-type="location"]):

Step

UI

1

Address search + “Use my location”

2

Map + confirm button

3

Extended address metadata form (when capture_address_metadata is enabled in settings)

Built-in triggers:

  • Click [data-precise-location-widget]openAddresslyModal("location", 1)

  • Click [data-store-hours-widget]openAddresslyModal("store-hours")

  • Core events addressly:location-not-provided, addressly:location-prompt-user, addressly:location-permission-delayed → open location step 1

Out-of-zone modals are non-dismissable (data-dismissable="false"); others are dismissable (backdrop click / close buttons with data-action="close-addressly-modal").

Automatic modal state (reactToLocationUpdate)

After addressly:location-updated, the widget runs determineModalState()handleModalState(). You can mirror this for custom flows:

Action

Behavior

show_location_verification_modal

Location step 2 when verification required and not verified

show_update_location_modal

Location step 2 with temp data (e.g. far-from-location “change location”)

show_out_of_zone_modal / close_and_show_out_of_zone_modal

Out-of-zone modal

show_store_hours_modal

Store hours when closed + section checkout_disabled_out_of_hours

close_location_modal

Close location modal if no other modal needed

none

No automatic modal

Manual example (force verification step):

window._addressly.mainWidget.handleModalState({
action: "show_location_verification_modal",
});

Store-hours “already shown” tracking uses sessionStorage key addressly_store_modal_shown_stores (array of store IDs). Helpers: hasShownModalForStore, markModalShownForStore, clearModalTracking, clearModalTrackingForStore.


Events

All Addressly events are CustomEvents on document with a detail object.

Lifecycle

Event

When

detail highlights

addressly:core-ready

End of core.init()

location, assignedStore, assignedZone, timestamp, source

addressly:location-updated

After processLocationData()

Same as above — primary hook for confirmed location

addressly:location-resolved

Provisional pin/search (not yet confirmed)

provisional: true, placeData, source

User / permission flow

Event

When

addressly:location-not-provided

Auto-detect failed, no IP fallback

addressly:location-prompt-user

Strategy prompt-user on init

addressly:location-permission-granted

Geolocation succeeded

addressly:location-permission-denied

User denied geolocation

addressly:location-permission-delayed

Geolocation timeout

addressly:far-from-location

Saved location far from browser position (homepage) — distance, previousLocation, browserLocation

addressly:place-selected

Autocomplete place chosen — placeData

Example: listen for confirmed location

document.addEventListener("addressly:location-updated", (event) => {
const { location, assignedStore, assignedZone } = event.detail;

console.log("Deliver to:", location.address.address1);
console.log("Store:", assignedStore?.name);
console.log("Zone:", assignedZone?.name);

// Update your custom UI
document.querySelector("#my-delivery-label").textContent =
location.address.address1;
});

Example: provisional vs confirmed

document.addEventListener("addressly:location-resolved", (event) => {
if (!event.detail.provisional) return;
const temp = event.detail.placeData;
// User is still on confirm step — do not treat as final delivery address
});

document.addEventListener("addressly:location-updated", (event) => {
// Confirmed — safe for checkout / fulfillment logic
});

Cross-tab sync

Both core and widget listen for storage events on key addressly-location and refresh UI when another tab updates location.


Local storage & session storage

localStorage

Key

Written by

Contents

addressly-location

Core processLocationData()

Full location object (JSON)

addressly-assigned-store

Core setStore()

Store object (id, name, address, coordinates, store_timings, optional zone, assignmentMethod, etc.)

addressly-assigned-zone

Core setZone()

Matched delivery zone metaobject fields

addressly-location-permission

Widget

"granted" | "denied"

Reading location

function readAddresslyLocation() {
try {
const raw = localStorage.getItem("addressly-location");
return raw ? JSON.parse(raw) : null;
} catch {
return null;
}
}

// Or from core (respects guest vs logged-in address_id stripping)
const location = window._addressly?.core?.getLocationFromLocalStorage()
?? window._addressly?.core?.CURRENT_LOCATION;

Location object shape (typical)

{
"coordinates": {
"latitude": 40.7128,
"longitude": -74.006,
"map_location": "https://www.google.com/maps?q=40.7128,-74.006"
},
"address": {
"address1": "123 Main St",
"address2": "",
"city": "New York",
"province": "NY",
"province_code": "NY",
"zip": "10001",
"country": "United States",
"country_code": "US",
"first_name": null,
"last_name": null,
"phone": null
},
"customer_verified": true,
"address_id": "gid://shopify/CustomerAddress/...",
"address_label": "Home",
"address_type": "home",
"strategy": "browser_location"
}

Reading store & zone

const store = JSON.parse(
localStorage.getItem("addressly-assigned-store") || "null"
);
const zone = JSON.parse(
localStorage.getItem("addressly-assigned-zone") || "null"
);

// Prefer live core state after init (handles out-of-zone nulling)
const liveStore = window._addressly?.core?.ASSIGNED_STORE;
const liveZone = window._addressly?.core?.ASSIGNED_ZONE;

assigned-store is removed when no store applies (e.g. strict out-of-zone). Zone id field on stored zone: zone_id (used in cart as _sldZoneId).

sessionStorage

Key

Purpose

addressly-location-confirmed-in-session

Set after location-updated; suppresses far-from-location prompt

addressly-far-from-location

Far-from-location flow state

addressly-location-prompt-user

One-time prompt for prompt-user strategy

addressly_store_modal_shown_stores

JSON array of store IDs that already saw store-hours modal


Cart integration

When location changes, core updates cart attributes and line item properties (e.g. _latitude, _longitude, _assigned_store, _sldZone, _location_verified). Read expected values:

const attrs = window._addressly.core.getCartAttributes(); 
const props = window._addressly.core.getLineItemProperties();

Checkout buttons matching checkout_button_selectors get class addressly-action-disabled when shouldDisableCheckout() is true.


Custom experience patterns

1. Custom “Change delivery address” button

<button type="button" id="my-change-address">Change address</button>
document.getElementById("my-change-address")?.addEventListener("click", () => {
window._addressly?.mainWidget?.openAddresslyModal("location", 1);
});

2. Show delivery summary without the default widget

function renderDeliverySummary() {
const loc = window._addressly?.core?.CURRENT_LOCATION;
const store = window._addressly?.core?.ASSIGNED_STORE;
if (!loc) return "Add delivery address";
return `${loc.address.address1} · ${store?.name ?? "No store"}`;
}

document.addEventListener("addressly:location-updated", renderDeliverySummary);
document.addEventListener("addressly:core-ready", renderDeliverySummary);

3. Core-only flow (no modal)

Use when location_strategy is widget-triggered and you provide your own UI:

const core = window._addressly.core;
const position = await core.getCurrentLocation();
if (position) {
await core.processLocationData({ ...position, customer_verified: true });
}

4. Block checkout in custom drawer

function syncCheckoutCta() {
const { shouldDisable, reasons } =
window._addressly.core.shouldDisableCheckout();
document.querySelector("#my-checkout").disabled = shouldDisable;
document.querySelector("#my-checkout-hint").hidden = !reasons.includes(
"out_of_delivery_zone"
);
}

document.addEventListener("addressly:location-updated", syncCheckoutCta);
document.addEventListener("addressly:core-ready", syncCheckoutCta);

5. React to out-of-zone without using the default modal

document.addEventListener("addressly:location-updated", () => {
const core = window._addressly.core;
if (!core.isLocationInDeliveryZone()) {
// Show your own banner — avoid calling showOutOfZoneModal()
showCustomOutOfZoneBanner(window._addressly.coreSettings.out_of_zone_message);
}
});

DOM hooks

Selector / attribute

Use

[data-precise-location-widget]

Opens location modal (built-in click handler)

[data-store-hours-widget]

Opens store hours modal

[data-addressly-modal-backdrop]

Modal container; class open when visible

.addressly-modal[data-modal-type="…"]

Individual modals

data-action="close-addressly-modal"

Close handler (delegated on backdrop)

#main-current-location

Default widget address label

#main-delivery-title

Default widget title

Modal markup is rendered by Precise Location (addressly-main.liquid). Custom themes must include that block (or equivalent markup) for openAddresslyModal to work.


File reference

File

Role

assets/addressly.js

Core: location, zones, stores, cart, events

assets/addressly-widget.js

Main widget: modals, map, Places, display

assets/addressly-cart-helper.js

Cart fetch intercept + refreshCart

assets/addressly-address-helper.js

AddressHelper.build() for Google place parsing

blocks/addressly-main.liquid

Bootstrap + modal HTML

blocks/addressly-section.liquid

Storefront widget section


Troubleshooting

Issue

Check

mainWidget is undefined

Google Maps callback not fired; API key missing

core is undefined

addressly.js embed not loaded

Modal does not open

[data-addressly-modal-backdrop] missing from DOM

Events never fire

Listen on document, not window

Storage empty but cart has attrs

Location set only on cart; run processLocationData once

processLocationData no-ops

Missing address or coordinates.latitude/longitude

For schema defaults and merchant-facing settings, see block schemas in addressly-main.liquid and addressly-section.liquid.

Did this answer your question?