Customization
Slots, content overrides, themes, and CSS classes for AnySpend components
Four layers of customization, each targeting a different concern:
All core components (<AnySpend>, <AnySpendDeposit>, <AnySpendCheckout>, <AnySpendCheckoutTrigger>) accept slots, content, and theme props.
Slots
Slots let you replace entire UI sections with your own React components. Each slot receives props from AnySpend that you can use in your custom implementation.
typescriptinterface AnySpendSlots { /** Replace the main action/payment button */ actionButton?: (props: ActionButtonSlotProps) => ReactNode; /** Replace the connect wallet button */ connectWalletButton?: (props: ConnectWalletButtonSlotProps) => ReactNode; /** Replace the header */ header?: (props: { mode: "page" | "modal" }) => ReactNode; /** Replace the footer */ footer?: ReactNode; /** Replace the success screen */ successScreen?: (props: SuccessScreenSlotProps) => ReactNode; /** Replace the error screen */ errorScreen?: (props: ErrorScreenSlotProps) => ReactNode; /** Replace the checkout form panel (checkout only) */ checkoutForm?: (props: CheckoutFormComponentProps) => ReactNode; /** Replace the shipping method selector (checkout only) */ shippingSelector?: (props: ShippingSelectorSlotProps) => ReactNode; /** Replace the discount code input (checkout only) */ discountInput?: (props: DiscountInputSlotProps) => ReactNode; }
The checkoutForm, shippingSelector, and discountInput slots are only used in <AnySpendCheckout> and <AnySpendCheckoutTrigger>. See the Checkout Guide for details on form/shipping/discount features.
Slot prop interfaces
typescriptinterface ActionButtonSlotProps { onClick: () => void; disabled: boolean; loading: boolean; text: string; }
typescriptinterface ConnectWalletButtonSlotProps { onPayment: () => void; txLoading: boolean; connectedAddress?: string; paymentLabel: string; }
typescriptinterface SuccessScreenSlotProps { title: string; description: string; txHash?: string; orderId?: string; explorerUrl?: string; onDone: () => void; returnUrl?: string; returnLabel?: string; }
typescriptinterface ErrorScreenSlotProps { title: string; description: string; errorType: "failure" | "expired" | "refunded"; orderId?: string; onRetry?: () => void; onDone?: () => void; }
typescriptinterface CheckoutFormComponentProps { onSubmit: (data: Record<string, unknown>) => void; onValidationChange: (isValid: boolean) => void; formData: Record<string, unknown>; setFormData: (data: Record<string, unknown>) => void; }
typescriptinterface ShippingSelectorSlotProps { options: ShippingOption[]; selectedId: string | null; onSelect: (option: ShippingOption) => void; }
typescriptinterface DiscountInputSlotProps { onApply: (code: string) => Promise<DiscountResult>; appliedDiscount: DiscountResult | null; onRemove: () => void; loading: boolean; }
Slot examples
tsx<AnySpend recipientAddress="0x..." slots={{ actionButton: ({ onClick, disabled, loading, text }) => ( <button onClick={onClick} disabled={disabled} className="my-custom-button" > {loading ? <MySpinner /> : text} </button> ), }} />
tsx<AnySpend recipientAddress="0x..." slots={{ successScreen: ({ title, txHash, explorerUrl, onDone, returnUrl }) => ( <div className="my-success"> <Confetti /> <h2>{title}</h2> {txHash && ( <a href={explorerUrl} target="_blank" rel="noopener noreferrer"> View Transaction </a> )} {returnUrl ? ( <a href={returnUrl}>Continue Shopping</a> ) : ( <button onClick={onDone}>Done</button> )} </div> ), }} />
tsx<AnySpend recipientAddress="0x..." slots={{ header: ({ mode }) => ( <div className="my-header"> <img src="/logo.svg" alt="My App" /> {mode === "modal" && <CloseButton />} </div> ), footer: ( <div className="my-footer"> <span>Powered by MyApp</span> <a href="/terms">Terms</a> </div> ), }} />
tsx<AnySpend recipientAddress="0x..." slots={{ connectWalletButton: ({ onPayment, txLoading, connectedAddress, paymentLabel }) => ( <div className="my-wallet-section"> {connectedAddress ? ( <button onClick={onPayment} disabled={txLoading}> {txLoading ? "Processing..." : paymentLabel} </button> ) : ( <button onClick={onPayment}>Connect & Pay</button> )} </div> ), }} />
tsx<AnySpendCheckout {...checkoutProps} slots={{ checkoutForm: ({ formData, setFormData }) => ( <div className="my-custom-form"> <input type="email" value={formData.email || ""} onChange={(e) => setFormData({ ...formData, email: e.target.value })} placeholder="Email for receipt" /> <select value={formData.size || ""} onChange={(e) => setFormData({ ...formData, size: e.target.value })} > <option value="">Select size</option> <option value="S">Small</option> <option value="M">Medium</option> <option value="L">Large</option> </select> </div> ), }} />
Content
Override text and messages displayed during different transaction states. Each field accepts a string or ReactNode.
typescriptinterface AnySpendContent { // Success state successTitle?: string | ReactNode; successDescription?: string | ReactNode; // Failure state failureTitle?: string | ReactNode; failureDescription?: string | ReactNode; // Expired state expiredTitle?: string | ReactNode; expiredDescription?: string | ReactNode; // Refunded state refundedTitle?: string | ReactNode; refundedDescription?: string | ReactNode; // Processing state processingTitle?: string | ReactNode; processingDescription?: string | ReactNode; // Button labels returnButtonLabel?: string; retryButtonLabel?: string; }
Content examples
tsx<AnySpendCheckout recipientAddress="0x..." destinationTokenAddress="0x..." destinationTokenChainId={8453} items={cartItems} content={{ successTitle: "Order Confirmed!", successDescription: "Your order is being prepared. Check your email for shipping updates.", failureTitle: "Payment Failed", failureDescription: "Your card was not charged. Please try again or use a different payment method.", processingTitle: "Processing Payment...", processingDescription: "Please don't close this window. This usually takes under a minute.", returnButtonLabel: "Back to Store", retryButtonLabel: "Try Again", }} />
tsx<AnySpend recipientAddress="0x..." content={{ successTitle: "Subscription Activated!", successDescription: ( <div> <p>Welcome to Pro! Your account has been upgraded.</p> <ul> <li>Unlimited API calls</li> <li>Priority support</li> <li>Advanced analytics</li> </ul> </div> ), returnButtonLabel: "Go to Dashboard", }} />
tsx<AnySpendDeposit recipientAddress="0x..." destinationTokenAddress="0x..." destinationTokenChainId={8453} content={{ successTitle: "Tokens Deposited!", successDescription: "Your game balance has been updated. Jump back in!", expiredTitle: "Session Expired", expiredDescription: "Your deposit window has closed. Start a new deposit to continue.", returnButtonLabel: "Back to Game", }} />
Theme
Configure the visual appearance of AnySpend components with brand colors.
typescriptinterface AnySpendTheme { /** Primary brand color (hex). Applied as the main accent color. */ brandColor?: string; /** Individual color overrides (hex values) */ colors?: Partial<{ primary: string; // Main text color secondary: string; // Secondary text color tertiary: string; // Muted text color surfacePrimary: string; // Primary background color surfaceSecondary: string; // Secondary background (cards, panels) brand: string; // Accent/brand color (buttons, links) borderPrimary: string; // Primary border color borderSecondary: string; // Secondary border color }>; }
The brandColor prop is a shortcut that sets the brand color. If both brandColor and colors.brand are provided, colors.brand takes precedence.
Theme examples
tsx// Quick branding — just set your brand color <AnySpend recipientAddress="0x..." theme={{ brandColor: "#6366f1" }} />
tsx<AnySpend recipientAddress="0x..." theme={{ brandColor: "#10b981", colors: { primary: "#f9fafb", secondary: "#9ca3af", tertiary: "#6b7280", surfacePrimary: "#111827", surfaceSecondary: "#1f2937", borderPrimary: "#374151", borderSecondary: "#4b5563", }, }} />
tsx<AnySpendCheckout recipientAddress="0x..." destinationTokenAddress="0x..." destinationTokenChainId={8453} items={items} theme={{ colors: { brand: "#e11d48", // Rose accent surfaceSecondary: "#fef2f2", // Light rose background }, }} />
CSS class overrides
Apply custom CSS classes to specific elements within AnySpend components. Each component has its own class interface.
AnySpendClasses
Used by <AnySpend>:
typescriptinterface AnySpendClasses { container?: string; header?: string; tabSection?: string; cryptoPaySection?: string; cryptoReceiveSection?: string; panelOnramp?: string; paymentButton?: string; navigationBar?: string; }
AnySpendCheckoutClasses
Used by <AnySpendCheckout> and <AnySpendCheckoutTrigger>:
typescriptinterface AnySpendCheckoutClasses { root?: string; // Root container layout?: string; // Two-panel layout wrapper paymentColumn?: string; // Left column (payment + forms) cartColumn?: string; // Right column (cart/summary) }
typescript{ paymentPanel?: string; // Payment panel container paymentTitle?: string; // "Pay with" title paymentMethodSelector?: string; // Method selector container paymentMethodButton?: string; // Individual method button cryptoPanel?: string; // Crypto payment section tokenSelector?: string; // Token dropdown quoteDisplay?: string; // Quote/price display payButton?: string; // Pay button fiatPanel?: string; // Fiat payment section stripeForm?: string; // Stripe form container stripeSubmitButton?: string; // Stripe submit button coinbasePanel?: string; // Coinbase panel }
typescript{ cartPanel?: string; // Cart container cartTitle?: string; // Cart header cartItemRow?: string; // Individual item row cartItemImage?: string; // Item image cartItemName?: string; // Item name cartItemDescription?: string; // Item description cartItemPrice?: string; // Item price cartItemMetadata?: string; // Metadata container cartItemMetadataLabel?: string; // Metadata label cartItemMetadataValue?: string; // Metadata value cartSummary?: string; // Summary section cartSubtotal?: string; // Subtotal row cartTotal?: string; // Total row cartSummaryLine?: string; // Extra summary line cartSummaryLineLabel?: string; // Summary line label cartSummaryLineAmount?: string; // Summary line amount cartDiscount?: string; // Discount row }
typescript{ formPanel?: string; // Form panel container formField?: string; // Individual form field formFieldLabel?: string; // Field label formFieldInput?: string; // Field input shippingSelector?: string; // Shipping option selector discountInput?: string; // Discount code input addressForm?: string; // Address form }
typescript{ poweredBy?: string; // "Powered by" footer successPanel?: string; // Success screen returnButton?: string; // Return/redirect button orderStatusPanel?: string; // Order status tracker retryButton?: string; // Retry button transactionLink?: string; // Transaction explorer link }
AnySpendDepositClasses
Used by <AnySpendDeposit>:
typescriptinterface AnySpendDepositClasses { root?: string; chainSelection?: string; form?: string; balance?: string; options?: string; chainsList?: string; divider?: string; backButton?: string; }
AnySpendAllClasses
Combine multiple class interfaces together. Used by <AnySpendDeposit> and <AnySpendWorkflowTrigger>:
typescriptinterface AnySpendAllClasses { deposit?: AnySpendDepositClasses; anySpend?: AnySpendClasses; checkout?: AnySpendCheckoutClasses; cryptoPaySection?: CryptoPaySectionClasses; cryptoReceiveSection?: CryptoReceiveSectionClasses; panelOnramp?: PanelOnrampClasses; orderDetails?: OrderDetailsClasses; recipientSelection?: RecipientSelectionClasses; qrDeposit?: QRDepositClasses; warningText?: WarningTextClasses; tabSection?: TabSectionClasses; feeDetailPanel?: FeeDetailPanelClasses; pointsDetailPanel?: PointsDetailPanelClasses; orderDetailsCollapsible?: OrderDetailsCollapsibleClasses; transferCryptoDetails?: TransferCryptoDetailsClasses; }
Class override example
tsx<AnySpend recipientAddress="0x..." classes={{ container: "rounded-2xl shadow-xl border border-gray-200", header: "bg-gradient-to-r from-blue-500 to-purple-500 text-white", paymentButton: "bg-indigo-600 hover:bg-indigo-700 rounded-xl font-bold", }} />
tsx<AnySpendCheckout recipientAddress="0x..." destinationTokenAddress="0x..." destinationTokenChainId={8453} items={items} classes={{ layout: "max-w-4xl mx-auto", cartPanel: "bg-gray-50 rounded-xl p-6", paymentPanel: "bg-white rounded-xl shadow-sm", branding: "text-center py-4", }} />
Combining customizations
All customization layers work together. Here's a complete example:
tsx<AnySpendCheckout recipientAddress="0x..." destinationTokenAddress="0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913" destinationTokenChainId={8453} items={cartItems} organizationName="Acme Store" organizationLogo="/acme-logo.svg" // Theme — brand colors theme={{ brandColor: "#4f46e5", colors: { surfaceSecondary: "#f5f3ff", }, }} // Content — custom messages content={{ successTitle: "Thank you for your purchase!", successDescription: "Your order confirmation has been sent to your email.", returnButtonLabel: "Continue Shopping", }} // Slots — custom components slots={{ footer: ( <div className="flex items-center justify-center gap-2 py-4 text-sm text-gray-500"> <LockIcon /> <span>Secure checkout powered by AnySpend</span> </div> ), successScreen: ({ title, description, txHash, onDone }) => ( <div className="text-center p-8"> <ConfettiAnimation /> <h2 className="text-2xl font-bold">{title}</h2> <p className="text-gray-600 mt-2">{description}</p> <button onClick={onDone} className="mt-6 btn-primary"> Continue Shopping </button> </div> ), }} // Classes — CSS overrides classes={{ layout: "max-w-5xl mx-auto", cartPanel: "bg-indigo-50/50 rounded-2xl", }} />