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/react
    npm 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 you
    const 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 token
    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;

    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.