Prerequisites
Precise Location app embed block (
addressly-main.liquid) onbody— loads core, modal markup, Google Maps, andwindow._addresslybootstrap data.Addressly Widget section (
addressly-section.liquid) — optional but typical; renders the delivery widget andpreciseLocationSettings.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 |
|
|
|
|
|
|
| Main block settings (location strategy, zones, checkout rules) |
| Widget section settings (store hours UI, etc.) |
| Active store metaobjects |
| Delivery zone metaobjects |
| Logged-in customer + verified addresses |
| Maps / theming |
| Shopify template name |
| Uses device bridge for geolocation when |
| Refreshes cart from Shopify (from |
| Last known cart snapshot |
Addressly Core API
Access via window._addressly.core.
State (in memory)
Property | Description |
| Confirmed/saved location object (also in |
| Store assigned to current location |
| Delivery zone matched for current location |
| 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 |
|
|
| Requires |
| IP geolocation when auto-detect fails |
| Fires |
|
|
| Copy for out-of-zone modal |
|
|
| 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 |
| Address search + “Use my location” |
| Map + confirm button |
| Extended address metadata form (when |
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 |
| Location step 2 when verification required and not verified |
| Location step 2 with temp data (e.g. far-from-location “change location”) |
| Out-of-zone modal |
| Store hours when closed + section |
| Close location modal if no other modal needed |
| 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 |
|
| End of |
|
| After | Same as above — primary hook for confirmed location |
| Provisional pin/search (not yet confirmed) |
|
User / permission flow
Event | When |
| Auto-detect failed, no IP fallback |
| Strategy |
| Geolocation succeeded |
| User denied geolocation |
| Geolocation timeout |
| Saved location far from browser position (homepage) — |
| Autocomplete place chosen — |
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 |
| Core | Full location object (JSON) |
| Core | Store object ( |
| Core | Matched delivery zone metaobject fields |
| Widget |
|
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 |
| Set after |
| Far-from-location flow state |
| One-time prompt for |
| 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 |
| Opens location modal (built-in click handler) |
| Opens store hours modal |
| Modal container; class |
| Individual modals |
| Close handler (delegated on backdrop) |
| Default widget address label |
| 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 |
| Core: location, zones, stores, cart, events |
| Main widget: modals, map, Places, display |
| Cart fetch intercept + |
|
|
| Bootstrap + modal HTML |
| Storefront widget section |
Troubleshooting
Issue | Check |
| Google Maps callback not fired; API key missing |
|
|
Modal does not open |
|
Events never fire | Listen on |
Storage empty but cart has attrs | Location set only on cart; run |
| Missing |
For schema defaults and merchant-facing settings, see block schemas in addressly-main.liquid and addressly-section.liquid.
