Shipping, Payment & Offers Hooks
React hooks for shipping methods, live quotes, payment methods, personal offers and custom analytics events
useShippingMethods
Merchant shipping methods with resolved fixed prices. Pass the cart total / weight so tiered and free-shipping rules resolve.
import { useShippingMethods } from "@behio/storefront-sdk/react";
const { methods, isLoading } = useShippingMethods({
country: "CZ",
cartTotal: cart?.grandTotal,
});
// methods: ShippingMethodSummary[] with id, name, provider, price, isFreeShipping,
// freeShippingThreshold, deliveryType, supportsPickupPoints, etaDaysMin/Max, ...Methods with priceStrategy: "live_quote" have price: null, so resolve a real
price with useShippingQuote once the destination address is known. For
methods with supportsPickupPoints, pair with usePickupPoints and send the
chosen point as checkout.pickupPointId.
useShippingQuote
Live quote for a destination address. Disabled until destinationAddress.country
is set, so you can bind it directly to the checkout form state.
import { useShippingQuote } from "@behio/storefront-sdk/react";
const { quotes, isLoading } = useShippingQuote({
destinationAddress: { country: "CZ", zip: form.zip, city: form.city },
cartTotal: cart?.grandTotal,
});
// quotes: ShippingQuote[] with everything from ShippingMethodSummary plus
// available, reason, quoteId, expiresAtItems with available: false carry a reason (e.g. no_rate_returned), so grey
them out instead of hiding them.
usePaymentMethods
import { usePaymentMethods } from "@behio/storefront-sdk/react";
const { methods } = usePaymentMethods();
// methods: CheckoutPaymentMethod[] with id, name, provider, currencies, fee,
// publicConfig (redirectFlow / offline / bank account for transfers)Currency defaults to the active BehioProvider currency; methods that do not
support it are filtered server-side.
usePersonalOffers
Behavioral personal offers (Behio Analytics → Smart Offers). Consent-gated: the hook polls the analytics tracker for the visitor id and stays idle until analytics consent is granted, so it is always safe to mount.
import { usePersonalOffers } from "@behio/storefront-sdk/react";
const { offers, claimByEmail, isClaiming } = usePersonalOffers();
// offers: PersonalOffer[] with id, productId, percent, status, code, expiresAt
const offer = offers[0];
if (offer?.status === "EMAIL_PENDING") {
// e-mail gate: trade an e-mail for the code
const { code } = await claimByEmail(offer.id, email);
}status: "OFFERED" means code is ready to show; "EMAIL_PENDING" means the
merchant requires an e-mail first, so call claimByEmail, which also updates the
cached offer with the revealed code.
useVisitorMessages
Pull channel for merchant automation events (the storefront.event action in
the admin automation builder). Consent-gated like usePersonalOffers - the
hook stays idle until the analytics visitor id resolves, so it is always safe
to mount. Messages carry a merchant-defined name and free-form JSON
payload; the template decides how to react (modal, banner, toast).
import { useVisitorMessages } from "@behio/storefront-sdk/react";
const { messages, ack } = useVisitorMessages({
onMessage: async (message) => {
if (message.name === "sleva-modal") {
await showDiscountModal(message.payload);
await ack(message.id); // do not show it again on the next page view
}
},
});Every delivered message is also dispatched as a behio:visitor-message
CustomEvent on window (detail = the message), so plain JavaScript outside
React can react too:
window.addEventListener("behio:visitor-message", (e) => {
console.log(e.detail.name, e.detail.payload);
});Messages poll every 30 s by default (pollMs option); unacknowledged messages
stay queued until they expire, so a refresh never loses a modal the visitor
did not see.
useAnalyticsEvents
Thin wrapper over the fire-and-forget Behio Analytics ingest. Prefer mounting
BehioAnalyticsTracker (pageviews, consent and batching are handled for you);
use this hook for extra custom / ecommerce events on top.
import { useAnalyticsEvents } from "@behio/storefront-sdk/react";
const { track, trackEvent } = useAnalyticsEvents();
// Named custom event with props
trackEvent("newsletter_prompt_shown", { placement: "footer" });
// Raw event(s)
track({ type: "ecommerce", name: "begin_checkout", value: cart.grandTotal, currency: "CZK" });The consent-gated visitor id is attached automatically when available and all failures are swallowed, so analytics never breaks the shop.