@paypal/react-paypal-js vs @paypal/checkout-server-sdk vs @stripe/stripe-js vs react-payment-inputs
Architecting Payment Flows in React Applications
@paypal/react-paypal-js@paypal/checkout-server-sdk@stripe/stripe-jsreact-payment-inputs

Architecting Payment Flows in React Applications

These packages cover different layers of payment integration, ranging from server-side transaction validation to client-side UI components. @paypal/checkout-server-sdk handles backend logic for PayPal, while @paypal/react-paypal-js provides React components for PayPal buttons. @stripe/stripe-js is the core library for integrating Stripe payments in the browser, and react-payment-inputs offers generic UI hooks for building custom credit card forms. Understanding where each tool fits ensures secure and compliant payment architectures.

Npm Package Weekly Downloads Trend

3 Years

Github Stars Ranking

Stat Detail

Package
Downloads
Stars
Size
Issues
Publish
License
@paypal/react-paypal-js253,260318560 kB1810 days agoApache-2.0
@paypal/checkout-server-sdk0---4 years agoSEE LICENSE IN https://github.com/paypal/Checkout-NodeJS-SDK/blob/master/LICENSE
@stripe/stripe-js0734975 kB510 days agoMIT
react-payment-inputs0-267 kB-2 years agoMIT

Architecting Payment Flows: Server SDKs vs Frontend Libraries

Integrating payments requires mixing server-side security with client-side interactivity. The packages listed here serve different roles — some run on your backend to protect secrets, while others run in the browser to handle user input. Let's compare how they tackle common integration tasks.

🖥️ Runtime Environment: Backend vs Frontend

@paypal/checkout-server-sdk runs on your Node.js server.

  • Keeps secret keys safe from public view.
  • Validates transactions before capturing money.
// server-sdk: Node.js backend controller
const client = new paypal.core.PayPalEnvironment(clientId, clientSecret);
const request = new paypal.orders.OrdersCreateRequest();
request.prefer("return=representation");
request.requestBody({ intent: "CAPTURE", purchase_units: [{ amount: { currency_code: "USD", value: "100.00" } }] });
const response = await client.execute(request);

@paypal/react-paypal-js runs in the browser within React.

  • Loads PayPal scripts dynamically.
  • Renders buttons for user interaction.
// react-paypal-js: React component
import { PayPalScriptProvider, PayPalButtons } from "@paypal/react-paypal-js";
<PayPalScriptProvider options={{ clientId: "..." }}>
  <PayPalButtons createOrder={(data, actions) => {...}} />
</PayPalScriptProvider>

@stripe/stripe-js runs in the browser.

  • Loads Stripe.js securely.
  • Handles tokenization of card data.
// stripe-js: Client-side initialization
import { loadStripe } from "@stripe/stripe-js";
const stripe = await loadStripe("pk_test_...");
const result = await stripe.confirmCardPayment(clientSecret);

react-payment-inputs runs in the browser.

  • Provides UI hooks for card inputs.
  • Does not process payments itself.
// react-payment-inputs: Custom form hook
import { usePaymentInputs } from "react-payment-inputs";
const { getCardNumberProps } = usePaymentInputs();
<input {...getCardNumberProps()} />

🚀 Initializing the SDK

@paypal/checkout-server-sdk requires credentials in code.

  • You pass Client ID and Secret directly.
  • Never expose this to the browser.
// server-sdk: Secure server setup
const client = new paypal.core.PayPalEnvironment(clientId, clientSecret);

@paypal/react-paypal-js uses a Context Provider.

  • Wraps your app to manage script loading state.
  • Handles reloads and errors automatically.
// react-paypal-js: Provider wrapper
<PayPalScriptProvider options={{ clientId: "..." }}>
  <App />
</PayPalScriptProvider>

@stripe/stripe-js uses a standalone loader function.

  • Returns a promise that resolves to the stripe object.
  • Often wrapped in a React provider (react-stripe-js).
// stripe-js: Async loader
const stripe = await loadStripe("pk_test_...");

react-payment-inputs uses a React hook.

  • No external script loading required.
  • Manages local input state only.
// react-payment-inputs: Hook usage
const { getCardNumberProps, getExpiryDateProps } = usePaymentInputs();

🔒 Security & PCI Compliance

@paypal/checkout-server-sdk is fully secure by design.

  • Secrets stay on your server.
  • You control the entire flow.
// server-sdk: Server-side validation
const response = await client.execute(request);
// Validate response.status === "COMPLETED"

@paypal/react-paypal-js offloads security to PayPal.

  • Users log in to PayPal directly.
  • Your server never sees card details.
// react-paypal-js: OnApprove callback
onApprove={(data, actions) => {
  return actions.order.capture().then((details) => {
    // Send details to your server for verification
  });
}}

@stripe/stripe-js reduces PCI scope significantly.

  • Card data goes straight to Stripe.
  • You only handle tokens or payment intents.
// stripe-js: Confirming payment
const { error, paymentIntent } = await stripe.confirmCardPayment(clientSecret, {
  payment_method: { card: elements.getElement(CardElement) }
});

react-payment-inputs increases PCI scope.

  • You handle raw card numbers in your inputs.
  • Requires strict PCI DSS compliance on your server.
// react-payment-inputs: Handling raw data
const { meta } = usePaymentInputs();
// meta.number contains the raw card number - handle with care

📊 Summary Table

PackageEnvironmentPrimary RolePCI Scope
@paypal/checkout-server-sdkNode.js ServerTransaction ValidationLow (Server-side)
@paypal/react-paypal-jsReact BrowserPayPal ButtonsNone (Hosted Fields)
@stripe/stripe-jsBrowserStripe LogicLow (Elements)
react-payment-inputsBrowserUI Input MaskingHigh (Raw Data)

💡 Final Recommendation

@paypal/checkout-server-sdk is mandatory for any PayPal integration that validates payments on your backend. Pair it with @paypal/react-paypal-js for the frontend buttons.

@stripe/stripe-js is the standard for Stripe integrations. Use it with Stripe Elements to keep PCI compliance simple.

react-payment-inputs should only be used if you need a fully custom card form design and cannot use Stripe Elements. Be aware of the security burden.

Overall: Prefer provider-specific SDKs (react-paypal-js, stripe-js) over generic UI libs (react-payment-inputs) to reduce security risk and maintenance work.

How to Choose: @paypal/react-paypal-js vs @paypal/checkout-server-sdk vs @stripe/stripe-js vs react-payment-inputs

  • @paypal/react-paypal-js:

    Choose this package when building React frontends that require PayPal checkout buttons or smart payment fields. It manages script loading states and provides ready-made components that reduce boilerplate code. It is the official way to integrate PayPal UIs into a React application.

  • @paypal/checkout-server-sdk:

    Choose this package for Node.js backend services that need to create, validate, or capture PayPal orders securely. It keeps your client secrets off the client side and is essential for verifying transaction status before fulfilling orders. Do not use this in the browser as it exposes sensitive credentials.

  • @stripe/stripe-js:

    Choose this package for integrating Stripe payments into any JavaScript application, especially when paired with React. It loads the secure Stripe.js library and enables tokenization of card data without handling raw numbers. It is the foundation for using Stripe Elements or Payment Intents.

  • react-payment-inputs:

    Choose this package only if you need a fully custom credit card form design and cannot use provider-specific components like Stripe Elements. Be aware that using this increases your PCI compliance burden because you handle raw card data. It is best suited for projects where design control outweighs security convenience.

README for @paypal/react-paypal-js

react-paypal-js

React components for the PayPal JS SDK V6

build status npm version bundle size npm downloads apache license storybook

Using react-paypal-js version 8.x or earlier?

This documentation covers the V6 SDK integration introduced in v9.0.0. For the legacy integration using PayPalScriptProvider, PayPalButtons, PayPalHostedFields, and BraintreePayPalButtons, see README-v8.md.


Why use react-paypal-js?

The Problem

Integrating PayPal into React applications requires careful handling of SDK script loading, payment session management, and UI rendering. Building a robust integration from scratch can lead to issues with timing, state management, and buyer experience.

The Solution

react-paypal-js provides a modern, hooks-based solution that abstracts away the complexities of the PayPal V6 SDK. It enforces best practices by default to ensure buyers get the best possible user experience.

Features

  • Modern Hooks API - Fine-grained control over payment sessions with usePayPalOneTimePaymentSession, useVenmoOneTimePaymentSession, and more
  • Built-in Eligibility - Automatically check which payment methods are available with useEligibleMethods()
  • Web Component Buttons - Use PayPal's optimized <paypal-button>, <venmo-button>, and <paypal-pay-later-button> web components
  • Flexible Loading - Support for string token/id, Promise-based token/id, and deferred loading patterns
  • TypeScript Support - Complete type definitions for all components and hooks
  • SSR Compatible - Built-in hydration handling for server-side rendered applications

Supported Payment Methods

  • PayPal - Standard PayPal checkout
  • Venmo - Venmo payments
  • Pay Later - PayPal's buy now, pay later option
  • PayPal Basic Card - Guest card payments without a PayPal account
  • PayPal Subscriptions - Recurring billing subscriptions
  • PayPal Save - Vault payment methods without purchase
  • PayPal Credit - PayPal Credit one-time and save payments

Resources

Installation

npm install @paypal/react-paypal-js

Quick Start

import {
    PayPalProvider,
    PayPalOneTimePaymentButton,
} from "@paypal/react-paypal-js/sdk-v6";

function App() {
    return (
        <PayPalProvider
            clientId="your-client-id"
            components={["paypal-payments"]}
            pageType="checkout"
        >
            <CheckoutPage />
        </PayPalProvider>
    );
}

function CheckoutPage() {
    return (
        <PayPalOneTimePaymentButton
            createOrder={async () => {
                const response = await fetch("/api/create-order", {
                    method: "POST",
                });
                const { orderId } = await response.json();
                return { orderId };
            }}
            onApprove={async ({ orderId }: OnApproveDataOneTimePayments) => {
                await fetch(`/api/capture-order/${orderId}`, {
                    method: "POST",
                });
                console.log("Payment captured!");
            }}
        />
    );
}

PayPalProvider

The PayPalProvider component is the entry point for the V6 SDK. It handles loading the PayPal SDK, creating an instance, and running eligibility checks.

Props

PropTypeRequiredDescription
clientTokenstring | Promise<string>*Client token from your server. Mutually exclusive with clientId.
clientIdstring | Promise<string>*Client ID from your PayPal app. Mutually exclusive with clientToken.
componentsComponents[]NoSDK components to load. Defaults to ["paypal-payments"].
pageTypestringNoType of page: "checkout", "product-details", "cart", "product-listing", etc.
localestringNoLocale for the SDK (e.g., "en_US").
environment"sandbox" | "production"NoSDK environment.
merchantIdstring | string[]NoPayPal merchant ID(s).
clientMetadataIdstringNoClient metadata ID for tracking.
partnerAttributionIdstringNoPartner attribution ID (BN code).
shopperSessionIdstringNoShopper session ID for tracking.
testBuyerCountrystringNoTest buyer country code (sandbox only).
debugbooleanNoEnable debug mode.
dataNamespacestringNoCustom namespace for the SDK script data attribute.
eligibleMethodsResponseFindEligiblePaymentMethodsResponseNoServer-fetched eligibility response for SDK hydration (see Server-Side Rendering).

* Either clientToken or clientId is required, but not both. They are mutually exclusive.

Available Components

The components prop accepts an array of the following values:

  • "paypal-payments" - PayPal and Pay Later buttons
  • "venmo-payments" - Venmo button
  • "paypal-guest-payments" - Guest checkout (card payments)
  • "paypal-subscriptions" - Subscription payments

With Promise-based Client ID

function App() {
    // Memoize to prevent re-fetching on each render
    const clientIdPromise = useMemo(() => fetchClientId(), []);

    return (
        <PayPalProvider
            clientId={clientIdPromise}
            components={["paypal-payments"]}
            pageType="checkout"
        >
            <CheckoutPage />
        </PayPalProvider>
    );
}

Alternative: With Promise-based Token

function App() {
    // Memoize to prevent re-fetching on each render
    const tokenPromise = useMemo(() => fetchClientToken(), []);

    return (
        <PayPalProvider
            clientToken={tokenPromise}
            components={["paypal-payments"]}
            pageType="checkout"
        >
            <CheckoutPage />
        </PayPalProvider>
    );
}

Deferred Loading

function App() {
    const [clientId, setClientId] = useState<string>();

    useEffect(() => {
        fetchClientId().then(setClientId);
    }, []);

    return (
        <PayPalProvider
            clientId={clientId}
            components={["paypal-payments"]}
            pageType="checkout"
        >
            <CheckoutPage />
        </PayPalProvider>
    );
}

Tracking Loading State

Use the usePayPal hook to access the SDK loading status:

import {
    usePayPal,
    INSTANCE_LOADING_STATE,
} from "@paypal/react-paypal-js/sdk-v6";

function CheckoutPage() {
    const { loadingStatus, error } = usePayPal();

    if (loadingStatus === INSTANCE_LOADING_STATE.PENDING) {
        return <div className="spinner">Loading PayPal...</div>;
    }

    if (loadingStatus === INSTANCE_LOADING_STATE.REJECTED) {
        return (
            <div className="error">
                Failed to load PayPal SDK: {error?.message}
            </div>
        );
    }

    return <PayPalOneTimePaymentButton orderId="ORDER-123" />;
}

Button Components

PayPalOneTimePaymentButton

Renders a PayPal button for one-time payments.

import { PayPalOneTimePaymentButton } from "@paypal/react-paypal-js/sdk-v6";

<PayPalOneTimePaymentButton
    createOrder={async () => {
        const response = await fetch("/api/create-order", { method: "POST" });
        const { orderId } = await response.json();
        return { orderId };
    }}
    onApprove={async ({ orderId }: OnApproveDataOneTimePayments) => {
        await fetch(`/api/capture/${orderId}`, { method: "POST" });
        console.log("Payment approved!");
    }}
    onCancel={(data: OnCancelDataOneTimePayments) =>
        console.log("Payment cancelled")
    }
    onError={(data: OnErrorData) => console.error("Payment error:", data)}
    onComplete={(data: OnCompleteData) => console.log("Payment Flow Completed")}
/>;

Props:

PropTypeDescription
orderIdstringStatic order ID (alternative to createOrder)
createOrder() => Promise<{ orderId: string }>Async function to create an order
presentationMode"auto" | "popup" | "modal" | "redirect"How to present the payment session (default: "auto")
onApprove(data) => voidCalled when payment is approved
onCancel() => voidCalled when buyer cancels
onError(error) => voidCalled on error
onComplete(data) => voidCalled when payment session completes
type"pay" | "checkout" | "buynow" | "donate" | "subscribe"Button label type
disabledbooleanDisable the button

VenmoOneTimePaymentButton

Renders a Venmo button for one-time payments. Requires "venmo-payments" in the provider's components array.

import { VenmoOneTimePaymentButton } from "@paypal/react-paypal-js/sdk-v6";

<PayPalProvider
    clientId={clientId}
    components={["paypal-payments", "venmo-payments"]}
    pageType="checkout"
>
    <VenmoOneTimePaymentButton
        createOrder={async () => {
            const { orderId } = await createOrder();
            return { orderId };
        }}
        onApprove={(data: OnApproveDataOneTimePayments) =>
            console.log("Venmo payment approved!", data)
        }
        onCancel={(data: OnCancelDataOneTimePayments) =>
            console.log("Venmo payment cancelled", data)
        }
        onError={(data: OnErrorData) =>
            console.error("Venmo payment error:", data)
        }
        onComplete={(data: OnCompleteData) =>
            console.log("Venmo payment flow completed", data)
        }
    />
</PayPalProvider>;

PayLaterOneTimePaymentButton

Renders a Pay Later button for financing options. Country code and product code are automatically populated from eligibility data.

import { PayLaterOneTimePaymentButton } from "@paypal/react-paypal-js/sdk-v6";

<PayLaterOneTimePaymentButton
    createOrder={async () => {
        const { orderId } = await createOrder();
        return { orderId };
    }}
    onApprove={(data: OnApproveDataOneTimePayments) =>
        console.log("Pay Later approved!", data)
    }
    onCancel={(data: OnCancelDataOneTimePayments) =>
        console.log("Pay Later cancelled", data)
    }
    onError={(data: OnErrorData) => console.error("Pay Later error:", data)}
    onComplete={(data: OnCompleteData) =>
        console.log("Pay Later flow completed", data)
    }
/>;

PayPalGuestPaymentButton

Renders a guest checkout button for card payments without a PayPal account (Branded Card/Debit Card checkout). Requires "paypal-guest-payments" in the provider's components array.

import { PayPalGuestPaymentButton } from "@paypal/react-paypal-js/sdk-v6";

<PayPalProvider
    clientId={clientId}
    components={["paypal-payments", "paypal-guest-payments"]}
    pageType="checkout"
>
    <PayPalGuestPaymentButton
        createOrder={async () => {
            const { orderId } = await createOrder();
            return { orderId };
        }}
        onApprove={(data: OnApproveDataOneTimePayments) =>
            console.log("Guest payment approved!", data)
        }
        onCancel={(data: OnCancelDataOneTimePayments) =>
            console.log("Guest payment cancelled", data)
        }
        onError={(data: OnErrorData) =>
            console.error("Guest payment error:", data)
        }
        onComplete={(data: OnCompleteData) =>
            console.log("Guest payment flow completed", data)
        }
    />
</PayPalProvider>;

PayPalSavePaymentButton

Renders a button for vaulting a payment method without making a purchase.

import { PayPalSavePaymentButton } from "@paypal/react-paypal-js/sdk-v6";

<PayPalSavePaymentButton
    createVaultToken={async () => {
        const response = await fetch("/api/create-vault-token", {
            method: "POST",
        });
        const { vaultSetupToken } = await response.json();
        return { vaultSetupToken };
    }}
    onApprove={({ vaultSetupToken }: OnApproveDataSavePayments) => {
        console.log("Payment method saved:", vaultSetupToken);
    }}
    onCancel={(data: OnCancelDataSavePayments) =>
        console.log("Save payment cancelled", data)
    }
    onError={(data: OnErrorData) => console.error("Save payment error:", data)}
    onComplete={(data: OnCompleteData) =>
        console.log("Save payment flow completed", data)
    }
/>;

PayPalSubscriptionButton

Renders a PayPal button for subscription payments. Requires "paypal-subscriptions" in the provider's components array.

import { PayPalSubscriptionButton } from "@paypal/react-paypal-js/sdk-v6";

<PayPalProvider
    clientId={clientId}
    components={["paypal-subscriptions"]}
    pageType="checkout"
>
    <PayPalSubscriptionButton
        createSubscription={async () => {
            const response = await fetch("/api/create-subscription", {
                method: "POST",
            });
            const { subscriptionId } = await response.json();
            return { subscriptionId };
        }}
        onApprove={(data: OnApproveDataOneTimePayments) =>
            console.log("Subscription approved:", data)
        }
        onCancel={(data: OnCancelDataOneTimePayments) =>
            console.log("Subscription cancelled", data)
        }
        onError={(data: OnErrorData) =>
            console.error("Subscription error:", data)
        }
        onComplete={(data: OnCompleteData) =>
            console.log("Subscription flow completed", data)
        }
    />
</PayPalProvider>;

PayPalCreditOneTimePaymentButton

Renders a PayPal Credit button for one-time payments. The countryCode is automatically populated from eligibility data.

import { PayPalCreditOneTimePaymentButton } from "@paypal/react-paypal-js/sdk-v6";

<PayPalCreditOneTimePaymentButton
    createOrder={async () => {
        const response = await fetch("/api/create-order", { method: "POST" });
        const { orderId } = await response.json();
        return { orderId };
    }}
    onApprove={({ orderId }: OnApproveDataOneTimePayments) =>
        console.log("Credit payment approved:", orderId)
    }
    onCancel={(data: OnCancelDataOneTimePayments) =>
        console.log("Credit payment cancelled", data)
    }
    onError={(data: OnErrorData) =>
        console.error("Credit payment error:", data)
    }
    onComplete={(data: OnCompleteData) =>
        console.log("Credit payment flow completed", data)
    }
/>;

PayPalCreditSavePaymentButton

Renders a PayPal Credit button for saving a credit payment method (vaulting).

import { PayPalCreditSavePaymentButton } from "@paypal/react-paypal-js/sdk-v6";

<PayPalCreditSavePaymentButton
    createVaultToken={async () => {
        const response = await fetch("/api/create-vault-token", {
            method: "POST",
        });
        const { vaultSetupToken } = await response.json();
        return { vaultSetupToken };
    }}
    onApprove={(data: OnApproveDataSavePayments) =>
        console.log("Credit saved:", data)
    }
    onCancel={(data: OnCancelDataSavePayments) =>
        console.log("Credit save cancelled", data)
    }
    onError={(data: OnErrorData) => console.error("Credit save error:", data)}
    onComplete={(data: OnCompleteData) =>
        console.log("Credit save flow completed", data)
    }
/>;

Payment Flow

  1. User clicks a payment button
  2. handleClick() starts the payment session
  3. createOrder callback creates an order via your backend API
  4. PayPal opens the checkout experience (popup/modal/redirect)
  5. On approval, onApprove callback captures the order via the backend
  6. Success/error handling displays the result to the user

Hooks API

usePayPal

Returns the PayPal context including the SDK instance and loading status.

import {
    usePayPal,
    INSTANCE_LOADING_STATE,
} from "@paypal/react-paypal-js/sdk-v6";

function MyComponent() {
    const {
        sdkInstance, // The PayPal SDK instance
        eligiblePaymentMethods, // Eligible payment methods
        loadingStatus, // PENDING | RESOLVED | REJECTED
        error, // Any initialization error
        isHydrated, // SSR hydration status
    } = usePayPal();

    const isPending = loadingStatus === INSTANCE_LOADING_STATE.PENDING;
    const isReady = loadingStatus === INSTANCE_LOADING_STATE.RESOLVED;

    // ...
}

useEligibleMethods

Returns eligible payment methods and loading state. Use this to conditionally render payment buttons based on eligibility. This hook also updates the PayPalProvider reducer with Eligibility Output from the SDK, enabling built-in eligibility features in the UI Button components.

import { useEligibleMethods } from "@paypal/react-paypal-js/sdk-v6";

function PaymentOptions() {
    const { eligiblePaymentMethods, isLoading, error } = useEligibleMethods();

    if (isLoading) return <div>Checking eligibility...</div>;

    const isPayPalEligible = eligiblePaymentMethods?.isEligible("paypal");
    const isVenmoEligible = eligiblePaymentMethods?.isEligible("venmo");
    const isPayLaterEligible = eligiblePaymentMethods?.isEligible("paylater");

    return (
        <div>
            {isPayPalEligible && <PayPalOneTimePaymentButton {...props} />}
            {isVenmoEligible && <VenmoOneTimePaymentButton {...props} />}
            {isPayLaterEligible && <PayLaterOneTimePaymentButton {...props} />}
        </div>
    );
}

usePayPalMessages

Hook for integrating PayPal messaging (Pay Later promotions).

import { usePayPalMessages } from "@paypal/react-paypal-js/sdk-v6";

function PayLaterMessage() {
    const { error, isReady, handleFetchContent, handleCreateLearnMore } =
        usePayPalMessages({
            buyerCountry: "US",
            currencyCode: "USD",
        });

    // Use to display financing messages
}

Payment Session Hooks

For advanced use cases where you need full control over the payment flow, use the session hooks directly with web components.

Note: One-time payment session hooks (e.g., usePayPalOneTimePaymentSession) accept either a static orderId or a createOrder callback — they are mutually exclusive. Use orderId when you've already created the order, or createOrder to defer order creation until the buyer clicks. The same pattern applies to save payment hooks with vaultSetupToken vs createVaultToken.

HookPayment Type
usePayPalOneTimePaymentSessionPayPal
useVenmoOneTimePaymentSessionVenmo
usePayLaterOneTimePaymentSessionPay Later
usePayPalGuestPaymentSessionBasic Card
usePayPalSubscriptionPaymentSessionSubscriptions
usePayPalSavePaymentSessionSave Payment Method
usePayPalCreditOneTimePaymentSessionCredit (One-time)
usePayPalCreditSavePaymentSessionCredit (Save)

usePayPalOneTimePaymentSession

import { usePayPalOneTimePaymentSession } from "@paypal/react-paypal-js/sdk-v6";

function CustomPayPalButton() {
    const { isPending, error, handleClick } = usePayPalOneTimePaymentSession({
        createOrder: async () => {
            const { orderId } = await createOrder();
            return { orderId };
        },
        presentationMode: "auto",
        onApprove: (data: OnApproveDataOneTimePayments) =>
            console.log("Approved:", data),
        onCancel: (data: OnCancelDataOneTimePayments) =>
            console.log("Cancelled"),
        onError: (data: OnErrorData) => console.error(data),
        onComplete: (data: OnCompleteData) =>
            console.log("Payment session complete", data),
    });

    return (
        <paypal-button
            onClick={() => handleClick()}
            type="pay"
            disabled={isPending || error !== null}
        />
    );
}

useVenmoOneTimePaymentSession

import { useVenmoOneTimePaymentSession } from "@paypal/react-paypal-js/sdk-v6";

function CustomVenmoButton() {
    const { handleClick } = useVenmoOneTimePaymentSession({
        createOrder: async () => {
            const { orderId } = await createOrder();
            return { orderId };
        },
        onApprove: (data: OnApproveDataOneTimePayments) =>
            console.log("Approved:", data),
        onCancel: (data: OnCancelDataOneTimePayments) =>
            console.log("Cancelled", data),
        onError: (data: OnErrorData) => console.error("Error:", data),
        onComplete: (data: OnCompleteData) =>
            console.log("Payment session complete", data),
    });

    return <venmo-button onClick={() => handleClick()} />;
}

usePayLaterOneTimePaymentSession

import { usePayLaterOneTimePaymentSession } from "@paypal/react-paypal-js/sdk-v6";

function CustomPayLaterButton() {
    const { handleClick } = usePayLaterOneTimePaymentSession({
        createOrder: async () => {
            const { orderId } = await createOrder();
            return { orderId };
        },
        onApprove: (data: OnApproveDataOneTimePayments) =>
            console.log("Approved:", data),
        onCancel: (data: OnCancelDataOneTimePayments) =>
            console.log("Cancelled", data),
        onError: (data: OnErrorData) => console.error("Error:", data),
        onComplete: (data: OnCompleteData) =>
            console.log("Payment session complete", data),
    });

    return <paypal-pay-later-button onClick={() => handleClick()} />;
}

usePayPalGuestPaymentSession

import { usePayPalGuestPaymentSession } from "@paypal/react-paypal-js/sdk-v6";

function CustomPayPalGuestButton() {
    const { handleClick, buttonRef } = usePayPalGuestPaymentSession({
        createOrder: async () => {
            const { orderId } = await createOrder();
            return { orderId };
        },
        onApprove: (data: OnApproveDataOneTimePayments) =>
            console.log("Approved:", data),
        onCancel: (data: OnCancelDataOneTimePayments) =>
            console.log("Cancelled", data),
        onError: (data: OnErrorData) => console.error("Error:", data),
        onComplete: (data: OnCompleteData) =>
            console.log("Payment session complete", data),
    });

    return (
        <paypal-basic-card-container>
            <paypal-basic-card-button
                ref={buttonRef}
                onClick={() => handleClick()}
            />
        </paypal-basic-card-container>
    );
}

usePayPalSavePaymentSession

import { usePayPalSavePaymentSession } from "@paypal/react-paypal-js/sdk-v6";

function CustomPayPalSaveButton() {
    const { handleClick } = usePayPalSavePaymentSession({
        createVaultToken: async () => {
            const { vaultSetupToken } = await createVaultToken();
            return { vaultSetupToken };
        },
        presentationMode: "popup",
        onApprove: (data: OnApproveDataSavePayments) =>
            console.log("Saved:", data),
        onCancel: (data: OnCancelDataSavePayments) =>
            console.log("Cancelled", data),
        onError: (data: OnErrorData) => console.error("Error:", data),
        onComplete: (data: OnCompleteData) =>
            console.log("Payment session complete", data),
    });

    return <paypal-button onClick={() => handleClick()} type="pay" />;
}

usePayPalSubscriptionPaymentSession

import { usePayPalSubscriptionPaymentSession } from "@paypal/react-paypal-js/sdk-v6";

function CustomPayPalSubscriptionButton() {
    const { handleClick } = usePayPalSubscriptionPaymentSession({
        createSubscription: async () => {
            const response = await fetch("/api/create-subscription", {
                method: "POST",
            });
            const { subscriptionId } = await response.json();
            return { subscriptionId };
        },
        onApprove: (data: OnApproveDataOneTimePayments) =>
            console.log("Subscription approved:", data),
        onCancel: (data: OnCancelDataOneTimePayments) =>
            console.log("Cancelled", data),
        onError: (data: OnErrorData) => console.error("Error:", data),
        onComplete: (data: OnCompleteData) =>
            console.log("Payment session complete", data),
    });

    return <paypal-button onClick={() => handleClick()} type="subscribe" />;
}

usePayPalCreditOneTimePaymentSession

For PayPal Credit one-time payments.

import { usePayPalCreditOneTimePaymentSession } from "@paypal/react-paypal-js/sdk-v6";

function CustomPayPalCreditButton() {
    const { handleClick } = usePayPalCreditOneTimePaymentSession({
        createOrder: async () => {
            const { orderId } = await createOrder();
            return { orderId };
        },
        onApprove: (data: OnApproveDataOneTimePayments) =>
            console.log("Credit approved:", data),
        onCancel: (data: OnCancelDataOneTimePayments) =>
            console.log("Cancelled", data),
        onError: (data: OnErrorData) => console.error("Error:", data),
        onComplete: (data: OnCompleteData) =>
            console.log("Payment session complete", data),
    });

    return <paypal-credit-button onClick={() => handleClick()} />;
}

usePayPalCreditSavePaymentSession

For saving PayPal Credit as a payment method.

import { usePayPalCreditSavePaymentSession } from "@paypal/react-paypal-js/sdk-v6";

function CustomPayPalCreditSaveButton() {
    const { handleClick } = usePayPalCreditSavePaymentSession({
        createVaultToken: async () => {
            const { vaultSetupToken } = await createVaultSetupToken();
            return { vaultSetupToken };
        },
        onApprove: (data: OnApproveDataSavePayments) =>
            console.log("Credit approved:", data),
        onCancel: (data: OnCancelDataSavePayments) =>
            console.log("Cancelled", data),
        onError: (data: OnErrorData) => console.error("Error:", data),
        onComplete: (data: OnCompleteData) =>
            console.log("Payment session complete", data),
    });

    return <paypal-credit-button onClick={() => handleClick()} />;
}

Web Components

The V6 SDK uses web components for rendering buttons. These are automatically typed when you import from @paypal/react-paypal-js/sdk-v6.

Available Web Components

ComponentDescription
<paypal-button>PayPal payment button
<venmo-button>Venmo payment button
<paypal-pay-later-button>Pay Later button
<paypal-basic-card-container>Guest checkout container
<paypal-basic-card-button>Guest checkout button
<paypal-credit-button>PayPal Credit button
<paypal-message>PayPal messaging component

Button Types

The type prop controls the button label:

  • "pay" - "Pay with PayPal" (default)
  • "checkout" - "Checkout with PayPal"
  • "buynow" - "Buy Now"
  • "donate" - "Donate"
  • "subscribe" - "Subscribe"
<paypal-button type="checkout" onClick={handleClick} />

Server-Side Rendering

The useFetchEligibleMethods function is available from the server export path for pre-fetching eligibility data on the server. Pass the response to PayPalProvider via the eligibleMethodsResponse prop to avoid a client-side eligibility fetch.

// app/checkout/page.tsx (Next.js server component)
import { useFetchEligibleMethods } from "@paypal/react-paypal-js/sdk-v6/server";
import { PayPalProvider } from "@paypal/react-paypal-js/sdk-v6";

export default async function CheckoutPage() {
    const eligibleMethodsResponse = await useFetchEligibleMethods({
        environment: "sandbox",
        headers: {
            Authorization: `Bearer ${clientToken}`,
            "Content-Type": "application/json",
        },
        payload: {
            purchase_units: [
                { amount: { currency_code: "USD", value: "100.00" } },
            ],
        },
    });

    return (
        <PayPalProvider
            clientId={clientId}
            pageType="checkout"
            eligibleMethodsResponse={eligibleMethodsResponse}
        >
            <CheckoutForm />
        </PayPalProvider>
    );
}

Migration from v8.x (Legacy SDK)

The v9.0.0 release introduces the V6 SDK with a new API. Here are the key differences:

v8.x (Legacy)v9.0.0 (V6 SDK)
PayPalScriptProviderPayPalProvider
PayPalButtonsPayPalOneTimePaymentButton or hooks
options={{ clientId }}clientId={clientId} or clientToken={clientToken}
createOrder returns orderIdcreateOrder returns { orderId }
@paypal/react-paypal-js@paypal/react-paypal-js/sdk-v6

Before (v8.x)

import { PayPalScriptProvider, PayPalButtons } from "@paypal/react-paypal-js";

<PayPalScriptProvider options={{ clientId: "test" }}>
    <PayPalButtons
        createOrder={() => {
            return fetch("/api/orders", { method: "POST" })
                .then((res) => res.json())
                .then((order) => order.id);
        }}
        onApprove={(data) => {
            return fetch(`/api/orders/${data.orderID}/capture`, {
                method: "POST",
            });
        }}
    />
</PayPalScriptProvider>;

After (v9.0.0)

import {
    PayPalProvider,
    PayPalOneTimePaymentButton,
} from "@paypal/react-paypal-js/sdk-v6";

<PayPalProvider clientId={clientId} pageType="checkout">
    <PayPalOneTimePaymentButton
        createOrder={async () => {
            const res = await fetch("/api/orders", { method: "POST" });
            const order = await res.json();
            return { orderId: order.id };
        }}
        onApprove={async ({ orderId }) => {
            await fetch(`/api/orders/${orderId}/capture`, { method: "POST" });
        }}
    />
</PayPalProvider>;

For the legacy API documentation, see README-v8.md.

TypeScript

This package includes full TypeScript definitions. Import types from the same path:

import type {
    // Web component props
    ButtonProps,
    PayLaterButtonProps,
    PayPalBasicCardButtonProps,
    PayPalCreditButtonProps,

    // Session hook props
    UsePayPalOneTimePaymentSessionProps,
    UseVenmoOneTimePaymentSessionProps,
    UsePayLaterOneTimePaymentSessionProps,
    UsePayPalGuestPaymentSessionProps,
    UsePayPalSubscriptionPaymentSessionProps,
    UsePayPalSavePaymentSessionProps,
    UsePayPalCreditOneTimePaymentSessionProps,
    UsePayPalCreditSavePaymentSessionProps,

    // Button component props
    PayPalSubscriptionButtonProps,
    PayPalCreditOneTimePaymentButtonProps,
    PayPalCreditSavePaymentButtonProps,

    // Enums
    INSTANCE_LOADING_STATE,
} from "@paypal/react-paypal-js/sdk-v6";

Web Component Types

The package automatically extends JSX types to include PayPal web components. No additional configuration is needed for React 17, 18, or 19.

Browser Support

This library supports all modern browsers. See the PayPal browser support documentation for the full list.