How To Build a Stripe Form with React
Learn how build a React form to collect Stripe payments.
1Prepare your project
Install the StaticKit React library and Stripe libraries in your project:
npm install @statickit/reactnpm install @stripe/react-stripe-js @stripe/stripe-js
Add the StaticKitProvider
and Stripe Elements
components to your top-level app component. For example, if you are using Next.js, here's what your pages/_app.js
file might look like:
import { StaticKitProvider } from '@statickit/react';import { loadStripe } from '@stripe/stripe-js';import { Elements } from '@stripe/react-stripe-js';// This will inject the Stripe.js script on your site for youconst stripePromise = loadStripe('REPLACE_WITH_STRIPE_PUBLISHABLE_KEY');function App({ Component, pageProps }) {return (<StaticKitProvider site="{your-site-id}"><Elements stripe={stripePromise}><Component {...pageProps} /></Elements></StaticKitProvider>);}export default App;
2Add a charge function
In your statickit.json
file, add a Stripe function for creating a charge:
{"functions": {"createCharge": {"app": "stripe","type": "createCharge"}}}
Then, deploy your changes to StaticKit:
statickit deploy -k <your-deploy-key>
You'll be prompted with instructions on how to save your Stripe secret key.
Once successful, this will install a @statickit/functions
library for you that exports a createCharge
function.
3Scaffold your form
Here's a barebones starting point:
import React from 'react';import { CardElement } from '@stripe/react-stripe-js';const PaymentForm = () => {const handleSubmit = e => {e.preventDefault();// Do the actual processing...};return (<form onSubmit={handleSubmit}><CardElement /><button type="submit">Pay</button></form>)};export default PaymentForm;
Right now, this form doesn't actually do anything except display a Stripe card element with a button.
4Tokenize the card
The first step is to tell Stripe to create a token from the given credit card:
import React, { useState } from 'react';import { CardElement, useStripe, useElements } from '@stripe/react-stripe-js';const PaymentForm = () => {const stripe = useStripe();const elements = useElements();const handleSubmit = e => {e.preventDefault();const { error, token } = await stripe.createToken(elements.getElement(CardElement));};return (<form onSubmit={handleSubmit}><CardElement /><button type="submit">Pay</button></form>)};export default PaymentForm;
5Create a charge
Next, we'll use the token to create a $25 Stripe charge.
import React, { useState } from 'react';import { CardElement, useStripe, useElements } from '@stripe/react-stripe-js';import { useStaticKit } from '@statickit/react';import { createCharge } from '@statickit/functions';const PaymentForm = () => {const stripe = useStripe();const elements = useElements();const client = useStaticKit();const handleSubmit = e => {e.preventDefault();const { error, token } = await stripe.createToken(elements.getElement(CardElement));const result = await createCharge(client, {source: token.id,amount: 2500});};return (<form onSubmit={handleSubmit}><CardElement /><button type="submit">Pay</button></form>)};export default PaymentForm;
6Handle card errors
So far, we haven't taken into account failure modes, like Stripe failing to create a token or the charge not succeeding.
Let's store card errors in component state and display them:
import React, { useState } from 'react';import { CardElement, useStripe, useElements } from '@stripe/react-stripe-js';import { useStaticKit } from '@statickit/react';import { createCharge } from '@statickit/functions';const PaymentForm = () => {const stripe = useStripe();const elements = useElements();const client = useStaticKit();const [stripeError, setStripeError] = useState(null);const handleSubmit = e => {e.preventDefault();const { error, token } = await stripe.createToken(elements.getElement(CardElement));setStripeError(error);if (!token) return;const result = await createCharge(client, {source: token.id,amount: 2500});};return (<form onSubmit={handleSubmit}><CardElement onChange={e => setStripeError(e.error)} />{stripeError && <div>{stripeError.message}</div>}<button type="submit">Pay</button></form>)};export default PaymentForm;
7Track form state
At the moment, we aren't doing anything special after the charge successfully goes through.
Let's implement some state to track whether our form is idle, submitting, or submitted:
import React, { useState } from 'react';import { CardElement, useStripe, useElements } from '@stripe/react-stripe-js';import { useStaticKit } from '@statickit/react';import { createCharge } from '@statickit/functions';const PaymentForm = () => {const stripe = useStripe();const elements = useElements();const client = useStaticKit();const [stripeError, setStripeError] = useState(null);const [formState, setFormState] = useState('idle');const handleSubmit = e => {e.preventDefault();setFormState('submitting');const { error, token } = await stripe.createToken(elements.getElement(CardElement));setStripeError(error);if (!token) return;const result = await createCharge(client, {source: token.id,amount: 2500});setFormState(result.status === 'ok' ? 'submitted' : 'idle');};if (formState === 'submitted') {return <div>Charge succeeded!</div>;}return (<form onSubmit={handleSubmit}><CardElement onChange={e => setStripeError(e.error)} />{stripeError && <div>{stripeError.message}</div>}<button type="submit" disabled={formState === 'submitting'}>Pay</button></form>)};export default PaymentForm;
8Attach to a customer
Sometimes it makes sense to create a Stripe customer record and attach the credit card to that customer for tracking purposes or for later use. We can adapt our existing form to do just that.
First, add a createCustomer
function to your statickit.json
file:
{"functions": {"createCharge": {"app": "stripe","type": "createCharge"},"createCustomer": {"app": "stripe","type": "createCustomer"}}}
Then, deploy your changes to StaticKit:
statickit deploy -k <your-deploy-key>
Finally, let's call our createCustomer
function in our form handler:
import React, { useState } from 'react';import { CardElement, useStripe, useElements } from '@stripe/react-stripe-js';import { useStaticKit } from '@statickit/react';import { createCharge, createCustomer } from '@statickit/functions';const PaymentForm = () => {const stripe = useStripe();const elements = useElements();const client = useStaticKit();const [stripeError, setStripeError] = useState(null);const [formState, setFormState] = useState('idle');const handleSubmit = e => {e.preventDefault();setFormState('submitting');const { error, token } = await stripe.createToken(elements.getElement(CardElement));setStripeError(error);if (!token) return;const { customerToken } = await createCustomer(client, {source: token.id});const result = await createCharge(client, {customerToken, // pass the customer token instead of the Stripe card tokenamount: 2500});setFormState(result.status === 'ok' ? 'submitted' : 'idle');};if (formState === 'submitted') {return <div>Charge succeeded!</div>;}return (<form onSubmit={handleSubmit}><CardElement onChange={e => setStripeError(e.error)} />{stripeError && <div>{stripeError.message}</div>}<button type="submit" disabled={formState === 'submitting'}>Pay</button></form>)};export default PaymentForm;
Take a look at our fully-styled examples here: https://stripe-react.now.sh (source)
Check out the Stripe reference to see all available function types.