Custom Checkout

While the CheckoutPage component provides a complete, ready-to-use checkout solution, you may need more control over the layout, styling, or user experience. This guide explains how to build a completely custom checkout flow by using the library's hooks and context, while still leveraging the core functionality.

The example above demonstrates a fully customized checkout page with custom styling, where we use the library's PaymentMethods component (which is required) and build custom components for customer information, delivery address, and the submit button.

Steps to Build

When building a custom checkout flow, you'll need to:

  1. Wrap your app with ClickarooProvider at the root level to manage Clickaroo parameters
  2. Wrap your components in CheckoutProvider to access the checkout context
  3. Use hooks (useCheckoutContext, useOrderSubmission) to access and update form data
  4. Use the PaymentMethods component from the library (required - cannot be customized)
  5. Update form data through updateCustomerInfo and updateDeliveryAddress methods
  6. Submit orders using the useOrderSubmission hook

Form Data Structures

When customizing components, you need to ensure all required fields are updated correctly. The library uses two main data structures: CustomerInfo and DeliveryAddress. Understanding these structures is crucial for building custom components that work seamlessly with the checkout flow. CustomerInfo Fields

interface CustomerInfo {
  firstName: string;  // First name (required)
  lastName: string;   // Last name (required)
  email: string;      // Email (required, must be valid email format)
  phone: string;      // Phone (required, E.164 format)
}

DeliveryAddress Fields

interface DeliveryAddress {
  country: string;    // Country (required)
  address: string;    // Street address (required)
  address2?: string;  // Address line 2 (optional)
  city: string;       // City (required)
  state: string;      // State/Province (required)
  zipCode: string;    // Postal code (required)
}

Notes: When customizing components, you must update all required fields through updateCustomerInfo and updateDeliveryAddress.

Building Custom Components

Now let's see how to build custom components step by step. Each component uses hooks to access and update the checkout state.

Custom Customer Info

Use the useCheckoutContext hook to access customerInfo and updateCustomerInfo:

import { useCheckoutContext } from '@clickaroo/checkout-ui';
 
function CustomCustomerInfo() {
  const { customerInfo, updateCustomerInfo } = useCheckoutContext();
 
  return (
    <div>
      <input 
        value={customerInfo.firstName} 
        onChange={(e) => updateCustomerInfo({ firstName: e.target.value })} 
        placeholder="First Name"
      />
      <input 
        value={customerInfo.lastName} 
        onChange={(e) => updateCustomerInfo({ lastName: e.target.value })} 
        placeholder="Last Name"
      />
      <input 
        type="email" 
        value={customerInfo.email} 
        onChange={(e) => updateCustomerInfo({ email: e.target.value })} 
        placeholder="Email"
      />
      <input 
        type="tel" 
        value={customerInfo.phone} 
        onChange={(e) => updateCustomerInfo({ phone: e.target.value })} 
        placeholder="Phone"
      />
    </div>
  );
}

Custom Delivery Address

Similarly, use updateDeliveryAddress to update address information:

import { useCheckoutContext } from '@clickaroo/checkout-ui';
 
function CustomDeliveryAddress() {
  const { deliveryAddress, updateDeliveryAddress } = useCheckoutContext();
 
  return (
    <div>
      <input 
        value={deliveryAddress.country || ''} 
        onChange={(e) => updateDeliveryAddress({ country: e.target.value })} 
        placeholder="Country"
      />
      <input 
        value={deliveryAddress.address || ''} 
        onChange={(e) => updateDeliveryAddress({ address: e.target.value })} 
        placeholder="Street Address"
      />
      <input 
        value={deliveryAddress.city || ''} 
        onChange={(e) => updateDeliveryAddress({ city: e.target.value })} 
        placeholder="City"
      />
      <input 
        value={deliveryAddress.state || ''} 
        onChange={(e) => updateDeliveryAddress({ state: e.target.value })} 
        placeholder="State/Province"
      />
      <input 
        value={deliveryAddress.zipCode || ''} 
        onChange={(e) => updateDeliveryAddress({ zipCode: e.target.value })} 
        placeholder="ZIP Code"
      />
    </div>
  );
}

Custom Submit Button

Use useOrderSubmission hook to handle order submission and isFormValid() to check form validity:

import { useCheckoutContext, useOrderSubmission } from '@clickaroo/checkout-ui';
 
function CustomSubmitButton() {
  const { isFormValid } = useCheckoutContext();
  const { isSubmitting, submitOrder } = useOrderSubmission();
 
  const handleSubmit = async () => {
    // Always check form validity before submitting
    if (!isFormValid()) {
      alert('Please complete all required fields');
      return;
    }
 
    const result = await submitOrder();
    
    if (result.success) {
      console.log('Order successful', result.orderCode);
      // Handle success (e.g., redirect to success page)
    } else {
      console.error('Order failed', result.message);
      // Handle error (e.g., show error message)
    }
  };
 
  return (
    <button 
      onClick={handleSubmit} 
      disabled={!isFormValid() || isSubmitting}
    >
      {isSubmitting ? 'Processing...' : 'Complete Order'}
    </button>
  );
}

Complete Example

Here's a complete example putting it all together:

import { 
  CheckoutProvider, 
  PaymentMethods,
  ClickarooProvider,
  useCheckoutContext,
  useOrderSubmission
} from '@clickaroo/checkout-ui';
 
// ... (include the custom components above)
 
function CustomCheckoutPage() {
  const cart = [
    {
      sku: "TEST001",
      offerPricePoint: "OPP-XXX",
    },
  ];
 
  return (
    <ClickarooProvider>
      <CheckoutProvider 
        baseUrl="https://api.clickaroo.io"
        cart={cart}
        onCheckoutInit={() => {
          console.log('Checkout initialized');
        }}
      >
        <CustomCustomerInfo />
        <CustomDeliveryAddress />
        {/* PaymentMethods is required - cannot be customized */}
        <PaymentMethods />
        <CustomSubmitButton />
      </CheckoutProvider>
    </ClickarooProvider>
  );
}