vtex.delivery-promise-components is a VTEX IO Store Framework app exporting blocks
that let shoppers set a postal code, choose delivery vs. pickup, and pick a pickup
point — gated to stores using the Delivery Promise platform service (closed beta).
Blocks: shopper-location-setter, shipping-method-selector, pickup-point-selector,
availability-badges. Extra React exports for host apps: PickupModalPresentational,
pickupSearchClient, pickupInPointPreference.
| File | What it defines |
|---|---|
manifest.json |
App identity (vtex.delivery-promise-components@1.1.1), builders (react, messages, docs, store), platform deps, registries (smartcheckout) |
store/interfaces.json |
Block interfaces, component bindings, content schemas (props) |
react/client.ts |
All HTTP calls to platform APIs — read first when changing data flow |
react/utils/cookie.ts |
shipping_info cookie + __RUNTIME__.segmentToken reading; helpers for country, orderform, facets |
react/constants.ts |
SHIPPING_INFO_COOKIE, DEFAULT_TRADE_POLICY = '1', Pixel event ids, error codes |
react/context/DeliveryPromiseContext.tsx |
Context shape (State), action union, providers, hook exports |
react/context/useDeliveryPromise.ts |
All business logic — action handler, side effects, session orchestration |
react/context/DeliveryPromiseProviderCore.tsx |
Mounts UnavailableItemsModal once, wires state/dispatch contexts |
react/pickupInPointPreference.ts |
localStorage pickup preference (key shared with vtex.search-result PLP) |
react/modules/suppressAutoGeolocationSession.ts |
sessionStorage flag controlling auto-geolocation after CLEAR_ZIPCODE |
react/modules/pixelHelper.ts |
OrderForm → Pixel cart-item mapper |
react/typings/ |
Platform typings — regenerated by Toolbelt; do not hand-edit |
messages/en.json |
Reference locale; all other locales must mirror keys (intl-equalizer) |
docs/README.md |
User-facing block reference (props, CSS handles) |
README.md |
Contributor guide (run, test, publish) |
DeliveryPromiseProvider (single instance, mounted via theme StoreWrapper) wires the
context, mounts UnavailableItemsModal once, and contains all blocks. Blocks register
themselves on mount (uiRegistry tracks { required } per block type). All mutations
go through useDeliveryPromise() which holds the only side-effect logic and is the
sole caller of react/client.ts. Each block must be a singleton per type — the
registry has no ref counting; the last mount wins if duplicates exist.
All HTTP traffic lives in react/client.ts, plus one Nominatim call in
react/components/ShopperLocationDetectorButton.tsx.
GET /api/checkout/pub/postal-code/{country}/{zipCode}?an={account}—getAddress. Resolves city +geoCoordinates; called before nearly every other Delivery Promise call.POST /api/checkout/pub/orderForm/{orderFormId}/attachments/shippingData—updateOrderForm/clearOrderFormShipping.GET /api/checkout/pub/orderForm/{orderFormId}—getCartProducts. Cart used by the availability gate.orderFormIdfromlocalStorage.orderform.
POST /api/sessions—updateSession/clearShippingSession. Writespublic.facets.valueas a;-joined string, e.g.zip-code=12345;country=BRA;coordinates=-23,-46;shipping=delivery;pickupPoint=abc.
GET /api/intelligent-search/v0/pickup-point-availability/trade-policy/{salesChannel}?zip-code=...&an=...&country=...—getPickups. Called withcredentials: 'omit'.GET /api/io/_v/api/intelligent-search/catalog_count?zip-code=...&coordinates=...—getCatalogCount. Iftotal === 0the postal code is rejected (PRODUCTS_NOT_FOUND_ERROR_CODE).
All return { unavailableItemIds }. Optional ?sc={salesChannel} is appended when defined.
POST /api/delivery-promises-bff/availability/deliveryorpickup?an={account}— default cart-availability check (header / ShopperLocationSetter flow).POST /api/delivery-promises-bff/availability/delivery?an={account}— PLP postal facet flow +SELECT_DELIVERY_SHIPPING_OPTION.POST /api/delivery-promises-bff/availability/pickupid?an={account}&pickupId={id}—UPDATE_PICKUP.
Body shape:
{ "location": { "zipCode": "...", "coordinate": { "longitude": ..., "latitude": ... }, "country": "..." },
"items": [ { "itemId": "<sku>", "productId": "<catalog>" } ] }GET https://nominatim.openstreetmap.org/reverse?format=json&lat=...&lon=...— only fromShopperLocationDetectorButton. Public unauthenticated endpoint subject to OSM usage policy.
This app keeps shopper location state in four places, deliberately, because the storefront, Intelligent Search, and Checkout each read from different sources.
- Constant:
SHIPPING_INFO_COOKIE = 'shipping_info'(react/constants.ts). - Written by
setCookieinclient.ts:updateSession, cleared inclearShippingSession. 30-min expiry,path=/. - Stores the same facets value as the session, with
;replaced by:(cookies disallow;):zip-code=12345:country=BRA:coordinates=-23,-46:shipping=delivery:pickupPoint=abc. - Read first by
getFacetsData(react/utils/cookie.ts), before falling back to the segment token. Code comment: "__RUNTIME__.segmentTokenis not reliable for the facets. It might not be updated."
- Read-only fallback; produced by the platform render runtime (base64-encoded JSON).
- Decoded by
getCountryCode(returnscountryCode) and as a fallback insidegetFacetsData(extractsfacets). - Why not the source of truth: segment token can lag; the custom cookie is updated synchronously by
updateSessionso reads see new values immediately.
- Authoritative storefront session.
- Read via
useRenderSession()to getsession.namespaces.store.channel.value(sales channel / trade policy; falls back toDEFAULT_TRADE_POLICY = '1'). - Written via
POST /api/sessionsto setpublic.facets.value. Propagates to Intelligent Search and other storefront services.
localStorage.orderform— read bygetOrderFormIdto talk to Checkout.localStorage['vtex.search.pickupInPoint']— pickup preference shared withvtex.search-resultPLP. Shape:{ id, friendlyName, address, postalCode }. Invalidated when storedpostalCode≠ current. Helpers:pickupInPointPreference.{readStoredPickupPreference,persistPickupPreference}.sessionStorage['vtex:delivery-promise:suppressAutoGeolocation']— set onCLEAR_ZIPCODE, cleared on successfulsubmitZipcode. Prevents re-prompting geolocation after the user explicitly cleared their location.
Defined in DeliveryPromiseContext.tsx, handled in useDeliveryPromise.ts.
| Action | Effect |
|---|---|
UPDATE_ZIPCODE |
Cart-availability check (BFF delivery or deliveryorpickup per args.cartAvailability), then submitZipcode. May location.reload() (deferred when shipping-method-selector is mounted as required). |
UPDATE_PICKUP |
BFF pickupid check, then selectPickup → updates session + reloads. Persists pickup preference. |
SELECT_DELIVERY_SHIPPING_OPTION |
BFF delivery check, then writes shipping=delivery to session + reloads. |
RESET_FULFILLMENT_METHOD |
Clears shipping/pickupPoint from session + reloads. |
CLEAR_ZIPCODE |
Clears Checkout shippingData, session facets, shipping_info cookie; sets suppressAutoGeolocation; reloads. |
REGISTER_*_BLOCK / UNREGISTER_*_BLOCK |
Track { required } flag in uiRegistry.{shopperLocation,shippingMethod}. |
REQUEST_OPEN_SHIPPING_METHOD_MODAL |
Increments shippingMethodModalRequestId so a sibling shipping-method-selector opens its modal. |
ABORT_UNAVAILABLE_ITEMS_ACTION |
Closes UnavailableItemsModal without removing items. |
CONTINUE_UNAVAILABLE_ITEMS_ACTION |
Removes unavailable items via vtex.order-items (fires removeFromCart Pixel event), then runs the deferred action. |
submitZipcode(reload)computeseffectiveReload = reload && !shippingMethodRequired.- If
shipping-method-selectoris mounted withrequired: true, reload is deferred so the method modal can open first. - When both location and method are
required, the method modal is not auto-requested at submit time (the location flow handles it afterwards) so modals don't stack.
| Dep | Use |
|---|---|
vtex.styleguide |
UI primitives (Modal, Button, Input). |
vtex.css-handles |
Stable theme-customization class anchors. |
vtex.render-runtime |
useRuntime() (account), useSSR(), __RUNTIME__.segmentToken. |
vtex.pixel-manager |
Fires removeFromCart; subscribes to item-added-to-cart-shipping-modal. |
vtex.device-detector |
Mobile/desktop UI adaptation. |
vtex.address-form |
Postal code input pieces. |
vtex.product-summary-context |
AvailabilityBadges reads product.deliveryPromisesBadges. |
vtex.order-items |
addItems/removeItem for the unavailable-items flow. |
vtex.session-client |
useRenderSession() for sales channel + session loading state. |
make dev # yarn install (root + react) + vtex setup
make lint # yarn lint (eslint, no --fix)
make test # cd react && yarn test (vtex-test-tools)
make coverage # cd react && yarn test --coverage (60% threshold in react/jest.config.js)
make check # lint + test (pre-PR gate)
make link # vtex link (uses active VTEX account/workspace)
make run # alias for make linkRoot yarn scripts: yarn lint, yarn format, yarn lint:locales (intl-equalizer), yarn test.
vtex-io-cli·vtex-io-app-structure·vtex-io-react-apps·vtex-store-framework-navigatorspecification+implementing(vtex-agent-skills) for the SDD Lite workflow
- GitHub MCP — cross-repo references, issues, PRs (declared in
.mcp.json). - Atlassian MCP — Jira/Confluence context when tracking against the roadmap.
Part of the is-io-specs multi-repo workspace alongside vtex.intelligent-search-api, vtex.search-graphql, vtex.search-resolver, vtex.search-result, and vtex.search-session. SpecKit artifacts (constitution.md, scope_of_work/, specs/<feature>/) live at the parent aggregator:
is-io-specs/
├── .specify/memory/constitution.md # team contract across all 6 apps
├── docs/scope_of_work/ # per-feature inputs to /speckit.specify
└── specs/<feature>/ # spec.md, plan.md, tasks.md, analysis.md
This repo intentionally has no local .specify/. Because this is a public repo, multi-repo storage is required (see the Multi-repo spec-kit extension).
- Never run
vtex link,vtex publish,vtex deploy, workspace promotion, or any account/workspace-changing Toolbelt command without explicit confirmation. - Ask before running
vtex setup --tooling— it can overwritereact/.eslintrcand root Prettier/ESLint config; this repo has custom rules (eslint-config-vtex,eslint-config-vtex-react). - Never modify
manifest.jsonversiondirectly — version bumps go throughvtex release <patch|minor|major> stable(Danger CI in.github/workflows/danger.yml). - Never modify
react/typings/— regenerated byvtex setup --typings --ignore-linked. - Ask before adding/removing
buildersinmanifest.json— affects what BuilderHub validates and packages.
- Single instance per block type. No ref counting; do not introduce multi-mount logic without a context redesign.
- Do not bypass the cart-availability gate in
useDeliveryPromise. It prevents shoppers from silently losing cart items when location/method changes. - Do not change the
shipping_infocookie format without coordinated changes togetFacetsData— Intelligent Search relies on the same facets value the session has, parsed from the cookie when the segment token lags. - Sales channel must come from
session.namespaces.store.channel.value, falling back toDEFAULT_TRADE_POLICY = '1'. Do not hard-code. - Do not call new external (non-VTEX) endpoints without review. The only one today is Nominatim from the geolocation button; adding more changes the privacy/legal surface.
docs/is owned by@vtex-apps/technical-writers;messages/by@vtex-apps/localization(see.github/CODEOWNERS). Coordinate cross-folder changes accordingly.messages/en.jsonis the reference locale. New keys must be added in all locales —yarn lint:locales(and the lint-staged hook onmessages/*.json) enforces this.- Do not commit secrets, account names, workspace names, or store-specific data. All per-store context lives in the platform.