Billing is currently in Beta and its APIs are experimental and may undergo breaking changes. To mitigate potential disruptions, we recommend pinning your SDK and clerk-js package versions.
The useCheckout() hook is used to create, manage, and confirm a checkout session for a user or an Organization's Subscription Plan. It provides the state of the current checkout process, such as its status and any errors, along with methods to initiate and complete the checkout.
There are two ways to use useCheckout():
In conjunction with <CheckoutProvider /> to create a shared checkout context. All child components inside the provider can then use useCheckout() to access or update the same checkout state.
On its own by passing configuration options directly to it. This is ideal for self-contained components that handle their own checkout flow without needing a shared context.
useCheckout() returns a { checkout } object. The checkout object contains the following properties. They are null until the checkout process is started by calling the start() method.
Name
clear
Type
() => void
Description
A function that clears the current checkout state from the cache.
The <CheckoutProvider /> component is a wrapper that provides a checkout context to its children, allowing checkout state to be shared across multiple components. Child components can access the checkout context by calling useCheckout().
For the best user experience and to prevent potential errors, always wrap components using useCheckout() with both <ClerkLoaded> and <SignedIn> components. This ensures that the user is properly authenticated and Clerk is fully initialized before accessing checkout functionality.
The useCheckout() hook can be used with a context provider for managing state across multiple components or as a standalone hook for more isolated use cases.
With <CheckoutProvider />
Standalone Hook
The following example shows the basic structure for a checkout flow. A parent component, <SubscriptionPage />, sets up the <CheckoutProvider /> and renders the checkout flow. A child component, <CheckoutFlow />, uses the useCheckout() hook to access the checkout state.
<SubscriptionPage />
<CheckoutFlow />
src/components/SubscriptionPage.tsx
import { CheckoutProvider } from'@clerk/clerk-react/experimental'import { ClerkLoaded } from'@clerk/clerk-react'import { CheckoutFlow } from'./CheckoutFlow'exportdefaultfunctionSubscriptionPage() {// `<CheckoutProvider />` sets the context for the checkout flow.// Any child component can now call `useCheckout()` to access this context.return ( <CheckoutProviderfor="user"planId="cplan_xxx"planPeriod="month"> <div> <h1>Upgrade Your Plan</h1> <p>You are about to subscribe to our monthly plan</p> <ClerkLoaded> <CheckoutFlow /> </ClerkLoaded> </div> </CheckoutProvider> )}
src/components/CheckoutFlow.tsx
import { useCheckout } from'@clerk/clerk-react/experimental'import { useState } from'react'import { useNavigate } from'react-router-dom'exportfunctionCheckoutFlow() {const { checkout } =useCheckout()const { status } = checkoutif (status ==='needs_initialization') {return <CheckoutInitialization /> }return ( <divclassName="checkout-container"> <CheckoutSummary /> <PaymentSection /> </div> )}functionCheckoutInitialization() {const { checkout } =useCheckout()const { start,fetchStatus } = checkoutreturn ( <buttononClick={start} disabled={fetchStatus ==='fetching'} className="start-checkout-button"> {fetchStatus ==='fetching'?'Initializing...':'Start Checkout'} </button> )}functionPaymentSection() {const { checkout } =useCheckout()const { isConfirming,confirm,finalize,error } = checkoutconst [isProcessing,setIsProcessing] =useState(false)const [paymentMethodId,setPaymentMethodId] =useState<string|null>(null)constnavigate=useNavigate()constsubmitSelectedMethod=async () => {if (isProcessing ||!paymentMethodId) returnsetIsProcessing(true)try {// Confirm checkout with payment methodawaitconfirm({ paymentSourceId: paymentMethodId, })// Calling `.finalize` enables you to sync the client-side state with the server-side state of your users.// It revalidates all authorization checks computed within server components.awaitfinalize({navigate: () =>navigate('/dashboard'), }) } catch (error) {console.error('Payment failed:', error) } finally {setIsProcessing(false) } }return ( <> {/* A component that lists a user's payment methods and allows them to select one. See an example: https://clerk.com/docs/reference/hooks/use-payment-methods#examples */} <PaymentMethodsonChange={setPaymentMethodId} /> {error && <div>{error.message}</div>} <buttontype="button"disabled={isProcessing || isConfirming} onClick={submitSelectedMethod}> {isProcessing || isConfirming ?'Processing...':'Complete Purchase'} </button> </> )}functionCheckoutSummary() {const { checkout } =useCheckout()const { plan,totals } = checkoutreturn ( <div> <h2>Order Summary</h2> <span>{plan?.name}</span> <span> {totals?.totalDueNow.currencySymbol} {totals?.totalDueNow.amountFormatted} </span> </div> )}
For simple, self-contained components, you can use useCheckout() by passing the configuration options directly to the hook. This avoids the need to wrap the component in a provider.
The following example shows an <UpgradeButton /> component that manages its own checkout flow.
src/components/UpgradeButton.tsx
import { useCheckout } from'@clerk/clerk-react/experimental'exportfunctionUpgradeButton({ planId, planPeriod,}: { planId:string planPeriod:'month'|'annual'}) {// Pass options directly to the hook when not using a provider.const { checkout } =useCheckout({ planId, planPeriod, for:'user', })const { start,status,isStarting,error } = checkoutconsthandleStartCheckout=async () => {try {awaitstart()// In a real app, you would now use the `externalClientSecret`// from the checkout object to render a payment form.console.log('Checkout started! Status:',checkout.status) } catch (e) {console.error('Error starting checkout:', e) } }return ( <div> <buttononClick={handleStartCheckout} disabled={isStarting}> {isStarting ?'Initializing...':`Upgrade to ${planPeriod} plan`} </button> {error && <pstyle={{ color:'red' }}>Error: {error.errors[0].message}</p>} </div> )}