How to integrate Stripe with React

Handling payments can be quite a complex task in applications and is considered very safety-critical. For this purpose, we'll be making use of an easy-to-integrate platform called Stripe to aid our payments in web applications.

Stripe

Stripe is a payment processing platform to help applications securely accept and manage online payments. Its developer-friendly API makes it one of the top choices in websites, and therefore, its integration with ReactJS is in high demand.

React and Stripe
React and Stripe

Setting up a React project

Prior to using Stripe in a React project, we'll have to set up a project first. This can be done by following the steps given here.

Alternatively, these two commands can be run to set up the project quickly.

npx create-react-app react-proj
cd react-proj

Styling with Tailwind

For adding styles to our code, we will be making use of Tailwind CSS, a highly customizable library that provides class names to add different styles.

You can learn how to set it up here.

Project structure

After the setup, the project structure should be similar to the given image.

Setting up Stripe

Now that we're ready to integrate Stripe into our React project, let's start by downloading the necessary dependencies.

Installing the Stripe package

Stripe provides a library specifically designed for React integration. To install it, we can run the following command in our main directory.

  1. Imports for our client-side code

npm install --save @stripe/react-stripe-js @stripe/stripe-js
  1. Imports for our server-side code

npm install stripe

Importing the Stripe package

Once the package is installed, we can import the required modules from the Stripe package into our React code. Depending on the requirements, the imports can differ.

import { loadStripe } from "@stripe/stripe-js";

Obtaining Stripe's API key

To establish a connection with Stripe's services, we will need an API key. Simply put, we'll make our application communicate with Stripe and allow it to handle the payments safely.

You can obtain an API key by creating an account on Stripe. In this Answer, we'll be using the test keys from the developer mode. Stripe provides us with two types of keys in this mode:

  1. A publishable key (used in client-side code).

  2. A secret key (used in server-side code).

Make sure to keep your API key secure and treat it as a sensitive piece of information.

Note: It's a good practice to keep the API keys in a .env file. Make sure to replace both the client and server side codes with your keys.

Code sample

We will be creating a full-stack application with ReactJS for the client-side code and Express for the server-side code. Our application will be able to render a simple payment form that is handled by Stripe's own logic, connected through our server.

Note: You will have to run the front-end code on one port and the back-end code on another. Make sure to replace the server endpoint and the API keys accordingly.

Client code

Our client code displays the payment form by integrating both React and Stripe. Let's take a lot at the files included in the code.

CheckoutForm.jsx file

import React, { useEffect, useState } from "react";
import { PaymentElement, useStripe, useElements } from "@stripe/react-stripe-js";
export default function CheckoutForm() {
const stripe = useStripe();
const elements = useElements();
const [message, setMessage] = useState(null);
const [isLoading, setIsLoading] = useState(false);
useEffect(() => {
if (!stripe) {
return;
}
const clientSecret = new URLSearchParams(window.location.search).get(
"payment_intent_client_secret"
);
if (!clientSecret) {
return;
}
stripe.retrievePaymentIntent(clientSecret).then(({ paymentIntent }) => {
setMessage(paymentIntent.status === "succeeded" ? "Your payment succeeded" : "Unexpected error occurred");
});
}, [stripe]);
const handleSubmit = async (e) => {
e.preventDefault();
if (!stripe || !elements) {
return;
}
setIsLoading(true);
const { error } = await stripe.confirmPayment({
elements,
confirmParams: {
return_url: "http://localhost:3000",
}
});
if (error && (error.type === "card_error" || error.type === "validation_error")) {
setMessage(error.message);
}
setIsLoading(false);
};
return (
<form onSubmit={handleSubmit}>
<p className="text-black mb-4">Complete your payment here!</p>
<PaymentElement />
<button className='bg-black rounded-xl text-white p-2 mt-6 mb-2' disabled={isLoading || !stripe || !elements}>
{isLoading ? "Loading..." : "Pay now"}
</button>
{message && <div>{message}</div>}
</form>
);
}
  • Let's first import the necessary modules, including React, useEffect, useState, PaymentElement, useStripe, and useElements, to handle the payment functionality in our code.

  • We define the CheckoutForm function component that handles the payment form. It utilizes the stripe and elements objects from useStripe and useElements hooks respectively. The component includes elements for payment details, a submit handler, and conditional rendering of messages.

  • In the useEffect hook, we retrieve payment_intent_client_secret and use stripe.retrievePaymentIntent() to get the payment intent's status. We can set the message through this.

  • The handleSubmit is called when the form is submitted. It confirms the payment using stripe.confirmPayment() and sets the error, if any.

  • Our return code includes the components for the UI interface.

stripePayment.js file

import React, { useState, useEffect } from "react";
import { loadStripe } from "@stripe/stripe-js";
import { Elements } from "@stripe/react-stripe-js";
import CheckoutForm from "./CheckoutForm";
const stripePromise = loadStripe(process.env.CLIENT_KEY);
export default function StripeCheckout() {
const [clientSecret, setClientSecret] = useState("");
useEffect(() => {
// replace this with your own server endpoint
fetch("https://ed-5374963090194432.educative.run/create-payment-intent", {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({ items: [{}] }),
})
.then((res) => {
if (!res.ok) {
throw new Error("Network response was not ok");
}
return res.json();
})
.then((data) => setClientSecret(data.clientSecret))
.catch((error) => {
console.log(error);
});
}, []);
const options = {
clientSecret,
};
return (
<div>
{clientSecret && (
<Elements options={options} stripe={stripePromise}>
<CheckoutForm />
</Elements>
)}
</div>
);
}
  • We import the necessary modules for the code.

  • We import the CheckoutForm component here.

  • We load the Stripe API by calling loadStripe and passing the Stripe client key (replace this with your own key).

  • Next, the StripeCheckout function component is defined, which manages the clientSecret state.

  • In the useEffect hook, we make a POST request to the server endpoint /create-payment-intent to get the client secret. The items parameter can be replaced with the actual order item/s.

  • We define the options object that contains the clientSecret.

  • Finally, we render the CheckoutForm component which is to be wrapped in Stripes' Elements component. The options and stripePromise are then passed as props. The CheckoutForm is only rendered when clientSecret is available.

Checkout.jsx file

import React from "react";
import StripeCheckout from "./stripePayment";
const Checkout = () => {
return (
<div className="font-mono text-white text-opacity-70 font-[700] text-opacity-90 h-screen flex justify-center items-center">
<div className='bg-white rounded-md p-12 bg-opacity-70'>
<StripeCheckout/>
</div>
</div>
);
};
export default Checkout;
  • We can then render our component in the Checkout.jsx file, which is called in App.js.

Server code

Now that our front-end code is complete, we simply have to define an endpoint to communicate with Stripe. Let's build our back-end in Express.

const app = express();
const stripe = require("stripe")(process.env.SERVER_KEY);
app.use(cors());
app.use(bodyParser.json());
const calculateOrderAmount = (items) => {
return 2000;
};
app.post("/create-payment-intent", async (req, res) => {
const { items } = req.body;
const paymentIntent = await stripe.paymentIntents.create({
amount: calculateOrderAmount(items),
currency: "usd",
automatic_payment_methods: {
enabled: true,
},
});
res.send({
clientSecret: paymentIntent.client_secret,
});
});
  • The following code calculates the amount of items sent to it (you can replace it with your own logic) and returns the clientSecret to the client-side code.

Complete code

Hurray! Our full-stack application is now complete.

module.exports = {
  content: ["./src/**/*.{html,js,jsx}"],
  theme: {
    extend: {},
  },
  plugins: [],
};

Payment demonstration

Our application looks like this at first glance. The payment component is provided by Stripe and can be customized according to our needs.

Note: We can test our application using test cards from Stripe's official documentation herehttps://docs.stripe.com/testing?testing-method=card-numbers.

Let's simply click the Pay now button without entering the required information. The component shows the appropriate errors.

We will now enter valid entries from Stripe's test data and see what the result is in the next step.

After submitting the information, our payment has now succeeded.

Note: You can check out our course on "Integration With Stripe API" here.

How well do you know Stripe’s integration with React JS?

Question

Why do we use the .env file to store our secret keys?

Show Answer

Free Resources

Copyright ©2025 Educative, Inc. All rights reserved