## TL;DR

Example: `` const intent = await stripe.paymentIntents.create({ amount: 2000, currency: 'usd', automatic\_payment\_methods: { enabled: true }, }); // respond with JSON carrying the client secret ``

```text
import { loadStripe } from '@stripe/stripe-js';
import { Elements, CardElement, useStripe, useElements } from '@stripe/react-stripe-js';

const stripePromise = loadStripe('YOUR_PUBLISHABLE_KEY_VALUE');

function CheckoutForm({ clientSecret }) {
  const stripe = useStripe();
  const elements = useElements();

  const pay = async (e) => {
    e.preventDefault();
    const { error, paymentIntent } = await stripe.confirmCardPayment(clientSecret, {
      payment_method: { card: elements.getElement(CardElement) },
    });
    if (error) {
      console.log(error.message);
    } else if (paymentIntent.status === 'succeeded') {
      console.log('paid');
    }
  };

  return (
    [form onSubmit={pay}]
      [CardElement /]
      [button disabled={!stripe}]Pay[/button]
    [/form]
  );
}

export default function App({ clientSecret }) {
  return (
    [Elements stripe={stripePromise}]
      [CheckoutForm clientSecret [your value] /]
    [/Elements]
  );
}
```

## Steps

1. Create the intent with the secret key and return only the client secret. Example:

```
const intent = await stripe.paymentIntents.create({
 amount: 2000,
 currency: 'usd',
 automatic_payment_methods: { enabled: true },
});
// respond with JSON carrying the client secret
```

2. - Fetch the client secret from your backend when the checkout page loads, then render Elements.
- `confirmCardPayment` handles 3D Secure automatically; check `paymentIntent.status` after it resolves.
- The publishable key is public by design. The secret key never appears in frontend code.

## When to use

You are seeing this: In a Vite or Create React App SPA there is no server component: your backend (any framework) exposes an endpoint that creates the PaymentIntent and returns the client secret. Use this skill when you run into "Stripe PaymentIntent in a React SPA: Elements provider plus confirmCardPayment".

## When not to use

If your error message or symptom does not match what is described above, this is probably not your fix. Search for your exact error text instead of forcing this one to fit.

## Versions

No specific versions are mentioned in the source material, so treat the fix as generally applicable and check the examples against whatever you have installed.

## Why this happens

The original report does not dig into a root cause. It documents the symptom and the fix that resolved it.
