Checkout

The checkout system is a multi-step flow: Contact → Delivery → Payment → Confirmation. All option values (delivery methods, countries, cities, payment methods) are fetched from store settings automatically — no options props needed.


Multi-Step Checkout

<CheckoutSettingsProvider>
  <CheckoutProvider>
    <CheckoutContactStep>
      <div class="space-y-4">
        <CheckoutEmailTextField />
        <CheckoutNameTextField />
        <CheckoutPhoneTextField />
        <CheckoutNotesTextArea />
      </div>
    </CheckoutContactStep>

    <CheckoutDeliveryStep>
      <div class="space-y-4">
        <CheckoutDeliveryMethodSegmentedControl>
          <SegmentedControlItems>
            <SegmentedControlItem>
              <SegmentedControlItemLabel />
            </SegmentedControlItem>
          </SegmentedControlItems>
        </CheckoutDeliveryMethodSegmentedControl>

        <CheckoutCountrySelect>
          <SelectTrigger>
            <SelectValue placeholder="Select country" />
          </SelectTrigger>
          <SelectContent>
            <SelectListbox />
          </SelectContent>
        </CheckoutCountrySelect>

        <CheckoutDeliveryCitySelect>
          <SelectTrigger>
            <SelectValue placeholder="Select city" />
          </SelectTrigger>
          <SelectContent>
            <SelectListbox />
          </SelectContent>
        </CheckoutDeliveryCitySelect>

        <CheckoutDeliveryZoneMethod>
          <CheckoutDeliveryZoneMethodLabel class="font-semibold" />
          <CheckoutDeliveryZoneMethodPrice />
        </CheckoutDeliveryZoneMethod>
      </div>
    </CheckoutDeliveryStep>

    <CheckoutPaymentStep>
      <div class="space-y-4">
        <CheckoutPaymentMethodRadioGroup>
          <CheckoutPaymentMethod method="mpesa">
            <RadioGroupItem value="mpesa">
              <RadioGroupItemLabel>M-Pesa</RadioGroupItemLabel>
              <RadioGroupItemDescription>Pay via mobile money</RadioGroupItemDescription>
            </RadioGroupItem>
          </CheckoutPaymentMethod>
          <CheckoutPaymentMethod method="stripe">
            <RadioGroupItem value="stripe">
              <RadioGroupItemLabel>Card</RadioGroupItemLabel>
              <RadioGroupItemDescription>Debit or credit card</RadioGroupItemDescription>
            </RadioGroupItem>
          </CheckoutPaymentMethod>
        </CheckoutPaymentMethodRadioGroup>

        <CheckoutPaymentPhoneTextField />

        <CheckoutSubmitProvider>
          <CheckoutButton>Place Order</CheckoutButton>
          <CheckoutLoading />
          <CheckoutErrorMessage />
        </CheckoutSubmitProvider>
      </div>
    </CheckoutPaymentStep>
  </CheckoutProvider>
</CheckoutSettingsProvider>

Payment Methods with Accordion

Each payment method expands to reveal its fields (M-Pesa shows a phone input, Stripe shows card details). The accordion trigger doubles as the selection control:

<Accordion>
  <CheckoutPaymentMethod method="mpesa">
    <AccordionItem value="mpesa">
      <AccordionTrigger>
        <CheckoutPaymentMethodSelectButton>
          <span>M-Pesa</span>
          <span class="text-xs text-muted-foreground">Pay via mobile money</span>
        </CheckoutPaymentMethodSelectButton>
      </AccordionTrigger>
      <AccordionContent>
        <div class="space-y-4 pt-4">
          <CheckoutPaymentPhoneTextField />
        </div>
      </AccordionContent>
    </AccordionItem>
  </CheckoutPaymentMethod>

  <CheckoutPaymentMethod method="stripe">
    <AccordionItem value="stripe">
      <AccordionTrigger>
        <CheckoutPaymentMethodSelectButton>
          <span>Card</span>
          <span class="text-xs text-muted-foreground">Debit or credit card</span>
        </CheckoutPaymentMethodSelectButton>
      </AccordionTrigger>
      <AccordionContent>
        <div class="space-y-4 pt-4">
          <p class="text-sm text-muted-foreground">Card details handled securely by Stripe</p>
        </div>
      </AccordionContent>
    </AccordionItem>
  </CheckoutPaymentMethod>
</Accordion>

<CheckoutSubmitProvider>
  <CheckoutButton>Place Order</CheckoutButton>
</CheckoutSubmitProvider>

CheckoutPaymentMethod gates each accordion item by store settings — if a payment method isn't enabled, its section is hidden. CheckoutPaymentMethodSelectButton writes to formData.paymentMethod and sets data-selected when active.


Delivery Method Selection

Three variants — use whichever fits your design:

{/* Dropdown */}
<CheckoutDeliveryMethodSelect>
  <SelectTrigger><SelectValue /></SelectTrigger>
  <SelectContent><SelectListbox /></SelectContent>
</CheckoutDeliveryMethodSelect>

{/* Radio group */}
<CheckoutDeliveryMethodRadioGroup>
  <RadioGroupItems>
    <RadioGroupItem>
      <RadioGroupItemLabel />
    </RadioGroupItem>
  </RadioGroupItems>
</CheckoutDeliveryMethodRadioGroup>

{/* Segmented control (pills) */}
<CheckoutDeliveryMethodSegmentedControl>
  <SegmentedControlItems>
    <SegmentedControlItem>
      <SegmentedControlItemLabel />
    </SegmentedControlItem>
  </SegmentedControlItems>
</CheckoutDeliveryMethodSegmentedControl>

Options are auto-fetched from delivery zones. No options prop needed.


Country & City Selection

Countries and cities are also auto-derived from delivery zones. Same three-variant pattern:

{/* Country */}
<CheckoutCountrySelect>
  <SelectTrigger><SelectValue placeholder="Country" /></SelectTrigger>
  <SelectContent><SelectListbox /></SelectContent>
</CheckoutCountrySelect>

{/* City — filtered by selected country */}
<CheckoutDeliveryCitySelect>
  <SelectTrigger><SelectValue placeholder="City" /></SelectTrigger>
  <SelectContent><SelectListbox /></SelectContent>
</CheckoutDeliveryCitySelect>

When a country is selected, the matching delivery zone auto-resolves and cities filter to that zone.


Delivery Zone

Once a country and delivery method are selected, show the resolved zone details:

<CheckoutDeliveryZoneName class="text-lg font-semibold" />

<CheckoutDeliveryZoneMethod>
  <div class="flex justify-between">
    <CheckoutDeliveryZoneMethodLabel />
    <CheckoutDeliveryZoneMethodPrice class="font-semibold" />
  </div>

  <p class="text-sm text-muted-foreground">
    <CheckoutDeliveryZoneMethodMinDays />
    <span>–</span>
    <CheckoutDeliveryZoneMethodMaxDays /> days
  </p>

  <CheckoutDeliveryZoneMethodConditions>
    <div class="flex justify-between">
      <CheckoutDeliveryZoneMethodConditionLabel />
      <CheckoutDeliveryZoneMethodConditionValue />
    </div>
  </CheckoutDeliveryZoneMethodConditions>

  <CheckoutDeliveryZoneMethodClassPrices>
    <div class="flex justify-between">
      <CheckoutDeliveryZoneMethodClassPriceLabel />
      <CheckoutDeliveryZoneMethodClassPriceValue />
    </div>
  </CheckoutDeliveryZoneMethodClassPrices>
</CheckoutDeliveryZoneMethod>

Custom Field Rendering

Contact fields are self-contained with default labels and validation. Override their rendering by passing children:

<CheckoutEmailTextField>
  <TextField class="flex flex-col gap-1">
    <TextFieldLabel class="font-semibold">Email Address</TextFieldLabel>

    <TextFieldInput class="border-2 p-3 rounded-lg" />
    <TextFieldErrorMessage class="text-red-500 text-sm" />
  </TextField>
</CheckoutEmailTextField>

The context binding (value, validation, onChange) is preserved — only the markup changes.


Address Fields

Shipping and billing address fields are available as standalone components:

<CheckoutShippingStreetField />
<CheckoutShippingCityField />
<CheckoutShippingStateField />
<CheckoutShippingZipField />
<CheckoutShippingCountryField />

<CheckoutBillingStreetField />
<CheckoutBillingCityField />
<CheckoutBillingStateField />
<CheckoutBillingZipField />
<CheckoutBillingCountryField />

Checkout Result

Show success or error state from the mutation:

<CheckoutResult />

Renders different content based on mutation success or error. Wrap in your own layout for custom styling.