Documentation
Feedback
Guides
Storefront Development

Storefront Development
FastStore
Storefront features
Delivery Promise
This feature is in closed beta, which means that only selected customers can access it. If you are interested in implementing it in the future, please contact our Support team.
In this guide, you’ll learn how to configure Delivery Promise in your FastStore project.
Delivery Promise is a feature that provides accurate and reliable delivery estimations for available products based on the customer location. It ensures that customers only see products that are in stock and can be delivered to their address or picked up at nearby locations.
This solution continuously updates product availability throughout the buying journey by responding to changes in catalog, pricing, inventory, or shipping settings. These updates are then sent to Checkout and Intelligent Search, ensuring customers see up-to-date options based on their region.
This feature introduces new hooks as part of the regionalization and location workflow:
  • useRegion: Accesses or updates the user's region information, such as postal code or location.
  • useRegionModal: Handles modal display logic for region selection, especially when the location input is required.
  • useGeoLocation: Uses the browser’s geolocation API or other client-side methods to attempt to auto-detect the user's location.

Default settings

Delivery Promise is defined in the deliveryPromise section of the discovery.config.js file.
Below are the default settings:
discovery.config.js

_10
deliveryPromise: {
_10
enabled: true,
_10
mandatory: false
_10
},

  • enabled: When set to true, Delivery Promise activates, allowing the store to provide delivery estimates based on the user’s location.
  • mandatory: When set to false, providing a postal code is optional for the user. Users can browse and add items to the cart without entering their postal code, though the accuracy of delivery information may be limited.

Before you begin

Get the search-session app

Check if you have installed the 0.5.0 or a later version of the search-session app.
  1. In your terminal, run the vtex list command.
  2. In the retrieved results, search for the app.
  3. If you don’t have this app installed, run the command vtex install vtex.search-session.

Enable the VTEX Intelligent Search app

To enable the Intelligent Search app, check the version of your Edition Store account.
In your terminal, log in to your account by running vtex login {accountName} and run vtex edition get to see the Edition app version installed. Remember to replace the values between curly braces with your account name.
Accounts in the vtex.edition-store@5.x
If your account is using the vtex.edition-store@5.x major, the VTEX Intelligent Search app is already included, and you need to integrate it via VTEX Admin.
To integrate the VTEX Intelligent Search, access Store Settings > Intelligent Search > Integrations in the VTEX Admin, and click START INTEGRATION.
Accounts using an edition version below 5.x
  1. In the VTEX IO CLI, log in to your VTEX account by running vtex login {accountName}. Remember to replace the values between curly braces with your account name.
  2. If the VTEX Intelligent Search app isn't installed in your account, run the following:

    _10
    vtex install vtex.search-resolver@1.x vtex.admin-search@1.x

After installing the app, integrate it by accessing Store Settings > Intelligent Search > Integrations in the VTEX Admin, and click START INTEGRATION.
Accounts that want to migrate to the 5.x
If you are using a different major version and want to migrate, please open a ticket with VTEX Support and request the installation of the Edition app vtex.edition-store@5.x.

Update the @faststore/cli package

Run the yarn upgrade @faststore/cli command to update the @faststore/cli package to its latest version.

Open a ticket with VTEX Support

Open a ticket with VTEX Support and request the activation of Delivery Promise in your account.

Instructions

Step 1 - Enabling deliveryPromise

  1. Open your FastStore project using the code editor of your choice.
  2. Open the discovery.config.js file.
  3. Under the deliveryPromise section, set the enabled value to true. Set the mandatory value based on your business needs. Learn more in the Default settings section.
    discovery.config.js

    _10
    deliveryPromise: {
    _10
    enabled: true,
    _10
    mandatory: false,
    _10
    }

Step 2 - Syncing FastStore project with Headless CMS

Run the faststore cms-sync command to sync the cms folder of your FastStore project with the Headless CMS.
The expected response is that you receive the message CMS synced successfully.... The native regionalization components and sections will be automatically updated. You can check them in the VTEX Admin:
  • Regionalization component: Go to Storefronts > Headless CMS > Global Sections > NavBar > Navigation > Regionalization.
  • Region Bar section: Go to Storefronts > Headless CMS > Global Sections > Region Bar.
  • Region Modal section: Go to Storefronts > Headless CMS > Global Sections > Region Modal.
You only need to add a new section, the Region Popover.

Step 3 - Adding Region Popover section

RegionPopover is a section that manages user location inputs. It's used when location data is missing or the store's default postal code is applied. Available in @faststore/core, RegionPopover uses the Popover component as its UI base.
{"base64":"  ","img":{"width":2851,"height":1135,"type":"png","mime":"image/png","wUnits":"px","hUnits":"px","length":1097984,"url":"https://vtexhelp.vtexassets.com/assets/docs/src/regionpopover___dedb0aad756657e71d4c8d40cb2ef5e7.png"}}
RegionPopover code
packages/core/src/components/region/RegionPopover/RegionPopover.tsx

_174
import type { PopoverProps as UIPopoverProps } from '@faststore/ui'
_174
import {
_174
Icon as UIIcon,
_174
InputField as UIInputField,
_174
Link as UILink,
_174
Popover as UIPopover,
_174
useUI,
_174
} from '@faststore/ui'
_174
import { useRef, useState } from 'react'
_174
_174
import useRegion from '../RegionModal/useRegion'
_174
_174
import { sessionStore, useSession } from 'src/sdk/session'
_174
import { textToTitleCase } from 'src/utils/utilities'
_174
import styles from './section.module.scss'
_174
_174
interface RegionPopoverProps {
_174
title?: UIPopoverProps['title']
_174
closeButtonAriaLabel?: UIPopoverProps['closeButtonAriaLabel']
_174
inputField?: {
_174
label?: string
_174
errorMessage?: string
_174
noProductsAvailableErrorMessage?: string
_174
buttonActionText?: string
_174
}
_174
idkPostalCodeLink?: {
_174
text?: string
_174
to?: string
_174
icon?: {
_174
icon?: string
_174
alt?: string
_174
}
_174
}
_174
textBeforeLocation?: string
_174
textAfterLocation?: string
_174
description?: string
_174
triggerRef?: UIPopoverProps['triggerRef']
_174
onDismiss: UIPopoverProps['onDismiss']
_174
offsetTop?: UIPopoverProps['offsetTop']
_174
offsetLeft?: UIPopoverProps['offsetLeft']
_174
placement?: UIPopoverProps['placement']
_174
}
_174
_174
function RegionPopover({
_174
title = 'Set your location',
_174
closeButtonAriaLabel,
_174
inputField: {
_174
label: inputFieldLabel,
_174
errorMessage: inputFieldErrorMessage,
_174
noProductsAvailableErrorMessage: inputFieldNoProductsAvailableErrorMessage,
_174
buttonActionText: inputButtonActionText,
_174
},
_174
idkPostalCodeLink: {
_174
text: idkPostalCodeLinkText,
_174
to: idkPostalCodeLinkTo,
_174
icon: { icon: idkPostalCodeLinkIcon, alt: idkPostalCodeLinkIconAlt },
_174
},
_174
textBeforeLocation = 'Your current location is:',
_174
textAfterLocation = 'Use the field below to change it.',
_174
description = 'Offers and availability vary by location.',
_174
triggerRef,
_174
offsetTop = 6,
_174
offsetLeft,
_174
placement = 'bottom-start',
_174
}: RegionPopoverProps) {
_174
const inputRef = useRef<HTMLInputElement>(null)
_174
const { isValidating, ...session } = useSession()
_174
const { popover: displayPopover, closePopover } = useUI()
_174
const { city, postalCode } = sessionStore.read()
_174
const location = city ? `${textToTitleCase(city)}, ${postalCode}` : postalCode
_174
_174
const [input, setInput] = useState<string>('')
_174
_174
const { loading, setRegion, regionError, setRegionError } = useRegion()
_174
_174
const handleSubmit = async () => {
_174
if (isValidating) {
_174
return
_174
}
_174
_174
await setRegion({
_174
session,
_174
onSuccess: () => {
_174
setInput('')
_174
closePopover()
_174
},
_174
postalCode: input,
_174
errorMessage: inputFieldErrorMessage,
_174
noProductsAvailableErrorMessage:
_174
inputFieldNoProductsAvailableErrorMessage,
_174
})
_174
}
_174
_174
const idkPostalCodeLinkProps = {
_174
href: idkPostalCodeLinkTo,
_174
children: (
_174
<>
_174
{idkPostalCodeLinkText}
_174
{!!idkPostalCodeLinkIcon && (
_174
<UIIcon
_174
name={idkPostalCodeLinkIcon}
_174
aria-label={idkPostalCodeLinkIconAlt}
_174
width={20}
_174
height={20}
_174
/>
_174
)}
_174
</>
_174
),
_174
}
_174
_174
const RegionPopoverContent = (
_174
<>
_174
<span data-fs-region-popover-description>
_174
{postalCode ? (
_174
<>
_174
{textBeforeLocation} <span>{location}</span>. {textAfterLocation}
_174
</>
_174
) : (
_174
<>{description}</>
_174
)}
_174
</span>
_174
<UIInputField
_174
data-fs-region-popover-input
_174
id="region-popover-input-postal-code"
_174
inputRef={inputRef}
_174
label={inputFieldLabel}
_174
actionable
_174
value={input}
_174
onInput={(e) => {
_174
regionError !== '' && setRegionError('')
_174
setInput(e.currentTarget.value)
_174
}}
_174
onSubmit={handleSubmit}
_174
onClear={() => {
_174
setInput('')
_174
setRegionError('')
_174
}}
_174
buttonActionText={loading ? '...' : inputButtonActionText}
_174
error={regionError}
_174
/>
_174
{idkPostalCodeLinkTo && (
_174
<UILink data-fs-region-popover-link {...idkPostalCodeLinkProps} />
_174
)}
_174
</>
_174
)
_174
_174
return (
_174
<>
_174
{displayPopover.isOpen && (
_174
<div className={`${styles.section} section-region-popover`}>
_174
<UIPopover
_174
data-fs-region-popover
_174
title={title}
_174
isOpen={displayPopover.isOpen}
_174
content={RegionPopoverContent}
_174
placement={placement}
_174
dismissible
_174
triggerRef={triggerRef}
_174
offsetTop={offsetTop}
_174
offsetLeft={offsetLeft}
_174
closeButtonAriaLabel={closeButtonAriaLabel}
_174
onEntered={() => {
_174
if (!postalCode && inputRef.current) {
_174
inputRef.current.focus()
_174
}
_174
}}
_174
/>
_174
</div>
_174
)}
_174
</>
_174
)
_174
}
_174
_174
export default RegionPopover

Follow the steps below to add the RegionPopover section to the Headless CMS.
  1. In the VTEX Admin, go to Storefront > Headless CMS > Global Sections.
  2. In the Sections tab, click + to open a modal with a list of section options.
  3. Search for the Region Popover section and click it to add.
  4. Set the Title and Close popover aria-label fields based on your scenario.
  5. Click Save to save your changes.
  6. Click Publish to open a dropdown list with the options Add to Release (to schedule the update) and Publish now (to publish immediately).

Step 4 - [Optional] Defining a default postal code

The default postal code is a fallback mechanism when the user doesn't provide a location, isn't logged in, or has no saved address.
To set a default postal code for your store, follow the steps below:
  1. Open your FastStore project using the code editor of your choice.
  2. Open the discovery.config.js file.
  3. In the session object, set the postalCode value based on your scenario. See the following example:
    discovery.config.js

    _15
    session: {
    _15
    currency: {
    _15
    code: "USD",
    _15
    symbol: "$",
    _15
    },
    _15
    locale: "en-US",
    _15
    channel: '{"salesChannel":"1","regionId":""}',
    _15
    country: "USA",
    _15
    deliveryMode: null,
    _15
    addressType: null,
    _15
    city: null,
    _15
    postalCode: 10021,
    _15
    geoCoordinates: null,
    _15
    person: null,
    _15
    },

Understanding location priority

To provide accurate Delivery Promise estimates, FastStore projects determine user location based on the following priority:
  1. User-provided location: If the user provided their location, such as postal code, this information is used.
  2. Logged-in user’s address: If the user hasn’t provided a postal code, we attempt to identify the location using the logged-in user's address.
  3. Default postal code: If the user hasn't provided a location, isn’t logged in, or has no saved address, we check if the merchant has configured a default postal code for all buyers. Learn to do so in Defining a default postal code.
  4. No postal code (not required): If no postal code is provided and not required, products are displayed without using Delivery Promise.
  5. No postal code (required): If no postal code is provided when it's required, a modal is displayed that the buyer can't close until a postal code is entered. To make the postal code required, open your discovery.config.js file and change the mandatory field in the deliveryPromise object to true, as shown in the Default settings section.
To better understand how this flow works, see the following diagram.
Contributors
1
Photo of the contributor
Was this helpful?
Yes
No
Suggest Edits (GitHub)
Contributors
1
Photo of the contributor
On this page