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:
- Wrap your app with
ClickarooProviderat the root level to manage Clickaroo parameters - Wrap your components in
CheckoutProviderto access the checkout context - Use hooks (
useCheckoutContext,useOrderSubmission) to access and update form data - Use the
PaymentMethodscomponent from the library (required - cannot be customized) - Update form data through
updateCustomerInfoandupdateDeliveryAddressmethods - Submit orders using the
useOrderSubmissionhook
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
updateCustomerInfoandupdateDeliveryAddress.
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>
);
}