Store Availability

Search and display store locations with real-time stock availability and opening hours.

For installation instructions, see Getting Started with Unify Widgets.

Basic Implementation

As a dialog (default)

<unify-store-availability-widget
  api-key="your-api-key"
  item-id="your-item-id"
></unify-store-availability-widget>

<button onclick="document.querySelector('unify-store-availability-widget').open = true">
  Where to buy
</button>

Inline (no dialog)

<unify-store-availability-widget
  api-key="your-api-key"
  item-id="your-item-id"
  no-dialog
></unify-store-availability-widget>

Component Attributes

AttributeTypeRequiredDescription
api-keyStringYesAPI key for Unify requests
item-idStringYesItem identifier for stock-store API calls
openBooleanNoControls dialog visibility when dialog mode is enabled
no-dialogBooleanNoRenders content inline instead of inside a dialog
demoBooleanNoEnables demo mode (X-Demo-Mode: true header)

Component Properties

PropertyTypeRequiredDescription
configObjectNoConfiguration object (see Config Options)

Config Options

Pass configuration via the config property to customize text labels, behavior, and thresholds.

document.querySelector('unify-store-availability-widget').config = {
  dialogTitle: 'Find in store',
  locale: 'en-US',
  maxRange: 20,
  // ... more options
};

Dialog & Layout

OptionDefaultDescription
dialogTitle"Store availability"Title of the search dialog
dialogDescription"Check the availability of your item in the store of your choice - if needed, contact the store."Description text below title

Search & Loading

OptionDefaultDescription
addressLabel"Address"Search input label
addressPlaceholder"Your address"Search input placeholder
submitButtonLabel"Submit"Search submit button text
searchingLabel"Searching..."Search loading state text
geolocateButtonLabel"Use my location"Geolocation button label
geolocatingLabel"Locating..."Geolocation loading state
loadingMessage"Searching for stores..."Text shown while stores are loading
validationErrorMessage"Please enter a full address including your city name"Address validation error message
searchErrorMessage"An error occurred while searching. Please try again."API error fallback
noStoresMessage"No stores found for the provided information."Empty result message

Search Options

OptionDefaultDescription
requireFullAddressfalseRequire at least one letter in address before submit (prevents postal code-only searches)
availableStockfalseInitial state: only display available stock
availableStockLabel"Only display available stock"Toggle label
hideAvailableStockTogglerfalseHide the stock filter checkbox
maxRange10Maximum search distance in km

Caching

The widget keeps an in-memory cache of stock responses, keyed by API key, item ID, demo mode, maxRange, and the searched address. A reopened dialog reuses fresh data instead of re-geolocating and re-fetching, avoiding flicker and redundant network calls. The cache lives for the lifetime of the element and is never persisted.

OptionDefaultDescription
stockCacheTtl300000Time (ms) a cached stock response stays valid (default 5 minutes). Set to 0 to disable caching entirely. Only successful responses are cached.

Store Display Options

OptionDefaultDescription
hideStoreAddressfalseHide store address in results
storeAddressLines1Number of address lines to display (when the address has multiple lines)
hideStoreContactfalseHide store phone number
hideOpeningHoursfalseHide open/closed status and hours
openingHoursDisplayMode"full""full" shows a static Open/Closed label; "simple" shows a live status computed from today's hours (e.g. "Open until 19:00")
storeOpenLabel"Open"Keyword shown for open stores, in both full and simple modes
storeClosedLabel"Closed"Keyword shown for closed stores, in both full and simple modes
storeOpenUntilLabel"until"Connector word inserted between the keyword and the time in simple mode while the store is currently open (e.g. "Open" + "until" + "19:00")
storeOpensAtLabel"- opens at"Connector word inserted between the keyword and the time in simple mode before opening or during a midday break (e.g. "Closed" + "- opens at" + "09:00")
storeOpeningHoursLabel"Opening Hours"Section heading for hours
noOpeningHoursMessage"No opening hours available"Fallback when hours unavailable
exceptionalDaysLabel"Exceptional Days"Header for holiday hours
informationLabel"Information"Header for extra opening hours info

Stock Display

OptionDefaultDescription
displayStockQuantitytrueShow quantity in stock labels
stockLimitedThreshold10Quantity threshold between "limited" and "available"
stockAvailableLabelWithQuantity"Available (%qty% remaining)"Available stock label with %qty%
stockAvailableLabel"Available"Available stock label without quantity
stockLimitedLabelWithQuantity"Limited stock (%qty% remaining)"Limited stock label with %qty%
stockLimitedLabel"Limited stock"Limited stock label without quantity
stockUnavailableLabel"Out of stock"Out-of-stock label

Click & Go Options

OptionDefaultDescription
showClickAndGoInfofalseShow click-and-go availability badge
storeClickAndGoAvailableLabel"⏱️ Click and Go Available"Click-and-go available label
storeClickAndGoNotAvailableLabel"🚫 Click and Go Not Available"Click-and-go unavailable label
showStorePickupButtonfalseShow "Store Pickup" button for each store
storePickupButtonLabel"Store Pickup"Button text for store pickup action

Custom Store CTAs

Beyond the built-in Store Pickup button, you can render any number of custom call-to-action buttons on each store card. Each click dispatches a store-custom-cta-clicked event (or a custom event name) carrying the store and CTA metadata, so you can wire the buttons to your own actions (favorite store, get directions, reserve, contact, etc.).

OptionDefaultDescription
customStoreCtas[]Array of custom CTA descriptors (see CTA object shape). Rendered in order after the Store Pickup button.
customStoreCtaDefaultLabel"Custom CTA"Fallback label used when a CTA has no label
customStoreCtaEventName"store-custom-cta-clicked"Event name dispatched when any custom CTA is clicked. Set a custom string to route all CTA clicks to your own event.

CTA object shape

Each entry in customStoreCtas is a plain object:

KeyTypeDefaultDescription
idStringcustom-cta-{n}Identifier for the CTA. Normalized to lowercase kebab-case ([a-z0-9_-]) and reused as the per-CTA CSS part suffix (store-custom-cta-button--{id}) and in the event detail.cta.id.
labelStringcustomStoreCtaDefaultLabelButton text
disabledBooleanfalseAlways render the button disabled
disableIfOutOfStockBooleanfalseDisable the button when the store has no stock (stock.quantity <= 0)
disableIfNoClickAndGoBooleanfalseDisable the button when the store does not support click & go
hideWhenDisabledBooleanfalseHide the button entirely instead of rendering it disabled
dataObjectnullArbitrary payload echoed back in the event detail.cta.data
document.querySelector('unify-store-availability-widget').config = {
  customStoreCtas: [
    { id: 'favorite-store', label: 'Add to favorites' },
    {
      id: 'reserve',
      label: 'Reserve in store',
      disableIfOutOfStock: true,
      hideWhenDisabled: true,
      data: { flow: 'reservation' },
    },
  ],
};

Formatting

OptionDefaultDescription
locale"en-US"Locale for date and number formatting (e.g., "fr-FR", "de-DE")
weekdayFormat"long"Weekday display format: "long" (Monday), "short" (Mon), "narrow" (M)

Error Handling

OptionDefaultDescription
errorMessage"An error occurred while fetching store data. Please try again later."Generic error message

Styling

The widget exposes CSS custom properties (prefixed --unify-) as the stable styling API, set on the widget element or any ancestor, and CSS ::part() selectors for structural elements (subject to change between versions — prefer custom properties where possible).

To discover what's available, inspect the <unify-store-availability-widget> element in your browser's DevTools:

  • Check the Computed tab for all --unify-* custom properties (editable live in the Styles panel)
  • Look inside the shadow DOM for elements carrying a part attribute — each is targetable via ::part()

Custom CTA button tokens (--unify-store-custom-cta-button-*) fall back to their Store Pickup equivalents (--unify-store-pickup-button-*) when unset. Custom CTA buttons also carry a per-id part (store-custom-cta-button--{id}) in addition to the shared store-custom-cta-button part, so a single CTA can be targeted individually.

Deprecated (inert): --unify-dialog-zindex and --unify-backdrop-zindex no longer have any effect — the dialog renders in the browser top layer via showModal(), which always paints above page content regardless of z-index.

Events

dialog-opened

Fired when the dialog open/close state changes.

document.querySelector('unify-store-availability-widget').addEventListener('dialog-opened', (event) => {
  console.log('Dialog is', event.detail.open ? 'open' : 'closed');
});

Detail shape:

{
  open: boolean  // true when opened, false when closed
}

store-pickup

Fired when user clicks the "Store Pickup" button (only dispatched if showStorePickupButton config option is true).

document.querySelector('unify-store-availability-widget').addEventListener('store-pickup', (event) => {
  console.log('Store selected:', event.detail.location.name);
  console.log('Item:', event.detail.itemId);
});

Detail shape:

{
  itemId: string,        // The item ID passed to the widget
  source: 'location',    // Always 'location'
  location: {            // Raw store object as returned by the API
    id: string,
    name: string,
    address: string,
    zip_code: string,
    city: string,
    distance: number,
    phone_number: string,
    stock: { quantity: number, status: string },
    opening_hours: { /* ... */ },
    click_and_go: boolean
  },
  store: {               // Normalized summary (camelCase, safe defaults)
    id: string | null,
    name: string | null,
    address: string | null,
    zipCode: string | null,
    city: string | null,
    phoneNumber: string | null,
    clickAndGo: boolean,
    closed: boolean,
    stockQuantity: number
  }
}

The raw API object is always available under location. The store key is a normalized, camelCase view with guaranteed keys and safe defaults — prefer it when forwarding data to your own systems.

store-custom-cta-clicked

Fired when the user clicks a custom CTA button. The event name can be overridden per-integration via the customStoreCtaEventName config option — if you set it, listen for that name instead.

document.querySelector('unify-store-availability-widget').addEventListener('store-custom-cta-clicked', (event) => {
  const { cta, store } = event.detail;
  console.log('CTA clicked:', cta.id, 'for store', store.name);
});

Detail shape:

Carries the same itemId, source, location, and store keys as store-pickup, plus a cta object describing the clicked button:

{
  itemId: string,
  source: 'location',
  location: { /* raw store object */ },
  store: { /* normalized summary — see store-pickup */ },
  cta: {
    id: string,          // Normalized CTA id
    label: string,       // Resolved label (falls back to customStoreCtaDefaultLabel)
    index: number,       // Position in the customStoreCtas array
    data: object | null  // The `data` payload from the CTA descriptor
  }
}

Examples

Basic setup

const widget = document.querySelector('unify-store-availability-widget');

widget.config = {
  dialogTitle: 'Find in store',
  storeOpenLabel: 'Open now',
  storeClosedLabel: 'Closed',
  maxRange: 25,
  locale: 'en-US',
};

// Open the search dialog
widget.open = true;

With Click & Go and Store Pickup

const widget = document.querySelector('unify-store-availability-widget');

widget.config = {
  dialogTitle: 'Availability near you',
  showClickAndGoInfo: true,
  showStorePickupButton: true,
  storePickupButtonLabel: 'Order for pickup',
  maxRange: 30,
};

// Handle store pickup action
widget.addEventListener('store-pickup', (event) => {
  const { location, itemId } = event.detail;
  console.log(`User selected ${location.name} for item ${itemId}`);
  // Send to your backend or order system
});

widget.open = true;

With custom CTAs

const widget = document.querySelector('unify-store-availability-widget');

widget.config = {
  customStoreCtas: [
    { id: 'directions', label: 'Get directions' },
    {
      id: 'reserve',
      label: 'Reserve in store',
      disableIfOutOfStock: true,
      data: { flow: 'reservation' },
    },
  ],
};

widget.addEventListener('store-custom-cta-clicked', (event) => {
  const { cta, store } = event.detail;
  if (cta.id === 'directions') {
    window.open(`https://maps.google.com/?q=${encodeURIComponent(store.address)}`);
  } else if (cta.id === 'reserve') {
    startReservation(store, cta.data);
  }
});

widget.open = true;

Styled with custom theme

<style>
  unify-store-availability-widget {
    /* Colors */
    --unify-dialog-bg: #f8f9fa;
    --unify-dialog-color: #212529;
    --unify-backdrop-bg: rgba(0, 0, 0, 0.6);
    
    /* Store status */
    --unify-store-status-open-color: #28a745;
    --unify-store-status-closed-color: #dc3545;
    
    /* Buttons */
    --unify-geolocate-button-bg: #007bff;
    --unify-geolocate-button-color: white;
    
    /* Spacing & sizing */
    --unify-input-border-radius: 8px;
    --unify-dialog-width: 700px;
  }
</style>

<unify-store-availability-widget
  api-key="your-api-key"
  item-id="your-item-id"
></unify-store-availability-widget>

Did this page help you?