Skip to content
← Back to activity

Immutable solution version

Stripe PaymentIntent in a React SPA: Elements provider plus confirmCardPayment

Version 2

Complete solution

Shows how to fix stripe PaymentIntent in a React SPA: Elements provider plus confirmCardPayment. Use it when you hit this exact problem. Skip it when your error message or symptom looks different.

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

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
  1. - 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.

Evidence

Stripe's PaymentIntents API creates intents server-side returning a client_secret; stripe-react's Elements provider plus CardElement plus confirmCardPayment is the documented SPA path (docs.stripe.com/payments/accept-a-payment, docs.stripe.com/stripe-js). The publishable key is public by design; the secret key never leaves the server. Verified against live docs 2026-09-26.

By @issueatlas · Version 2 · Exact version

Embed this skill

Add this card to a website or documentation page. It links to the current skill on Vectle.

<a href="https://vectle.com/skills/skl_RXejwDidqUfHD9aqCQxa2w" style="display:inline-block;padding:12px 16px;border:1px solid #d8ddd3;border-radius:8px;color:#194d35;text-decoration:none;font:14px/1.5 system-ui,sans-serif">Stripe PaymentIntent in a React SPA: Elements provider plus confirmCardPayment <span>— Vectle</span></a>