In this guide, we’ll use Swell-js to render Stripe Elements with React. Our objective is to lay the groundwork for a checkout that you can build upon to include additional custom logic you may wish to implement within your checkout flow.

For this project, you'll need:

  • A new or existing React project
  • Stripe connected as the payment gateway in your Swell store's Payment settings.

To get started, install Swell-js with the following command:

Install Swell.js
npm install --save swell-js

With this installed, we can move on to the card components.

To create a card component, create a Card.js component in src:

Create a card component
export default function Card() {
  return <div>Card</div>;
}

Once created, we can render our new card component in index.js:

Rendering the card component
import React from 'react';
import { createRoot } from 'react-dom/client';
import swell from 'swell-js';
import Card from './Card';
import './style.css';

swell.init('<store_id>', '<public_key>');

createRoot(document.getElementById('root')).render(<Card swell={swell} />);

In order to render Stripe Elements and tokenize cards using Swell.js, we’ll need to have a cart initialized and a product already in the cart. Swell.js handles the creation of Stripe paymentIntents automatically in the swell.payment.tokenize() method.

With the above in mind, let’s create a method to initialize a cart and add a product on initialization. In this case, you can imagine this as a direct “1-click” checkout.

Initialize a cart
async function createCart(swell) {
  await swell.cart.setItems([]); // Used to reset the cart on init
  await swell.cart.addItem({
    product_id: '<product_id>',
  });
}

Now we can call createCart() from a useEffect hook when the component mounts, adding the following logic to render the Card element:

Card element rendering logic
import { useEffect } from 'react';

export default function Card({ swell }) {
  useEffect(() => {
    async function initialize() {
      await createCart(swell);

      swell.payment.createElements({
        card: {
          elementId: '#card-element', // Default: #card-element
          options: {
            // Options are passed as a direct argument to stripe.js
            hidePostalCode: true,
          },
          onChange: (event) => {
            // Optional, called when the Element value changes
          },
          onReady: (event) => {
            // Optional, called when the Element is fully rendered
            console.log('ready');
          },
          onFocus: (event) => {
            // Optional, called when the Element gains focus
          },
          onBlur: (event) => {
            // Optional, called when the Element loses focus
          },
          onEscape: (event) => {
            // Optional, called when the escape key is pressed within an Element
          },
          onClick: (event) => {
            // Optional, called when the Element is clicked
            console.log('click');
          },
          onSuccess: (result) => {
            // Optional, called on card payment success
            console.log('success');
          },
          onError: (error) => {
            // Optional, called on card payment error
            console.log(error.message);
          },
        },
      });
    }

    initialize();
  }, [swell]);

  // ...
}

In order to render the Card element, update the component's returned JSX:

Edit the render() method
return (
  <div className="container">
    {/* Must match the elementId in swell.payment.createElements */}
    <div id="card-element" />
  </div>
);

Now that the rendering logic is in place, run npm start—you should be able to see your card element render.

Now we’ll have to add our tokenize method—this takes in the card details we’re passing into the Card element which creates the paymentIntent and paymentMethod in Stripe.

Tokenize method
const tokenize = () => {
  swell.payment.tokenize({
    card: {
      onSuccess: () => {
        console.log('tokenize success');
      },
      onError: (err) => {
        console.log(err);
      },
    },
  });
};

Next, create a button to call our tokenize() method. For this example, we’re separating out the tokenizing logic from the submit; however, you can also merge this logic into your submit function.

Using a button to call the tokenize() method
return (
  <div className="container">
    <div id="card-element" />
    <button onClick={tokenize}>Tokenize</button>
  </div>
);

Fill in a Stripe test card, and use Stripe's guide for testing cards for various scenarios while building your checkout. For a successful response, we can use 4242 4242 4242 4242 along with any future expiration date and CVC. The swell.payment.tokenize() method will attach the paymentIntent to the cart.

Once a card has been successfully tokenized, you can then submit the order. Create a submit() method to also update the cart with an email, as this is required to submit an order.

The submit() method
const submit = async () => {
  await swell.cart.update({
    account: {
      email: '<email>', // An email is required to submit an order
    },
  });
  const order = await swell.cart.submitOrder();
  console.log(order);
};

And we can now create a new button to call our submit() method:

Using a button to call the submit() method
return (
  <div className="container">
    <div id="card-element" />
    <button onClick={tokenize}>Tokenize</button>
    <button onClick={submit}>Submit</button>
  </div>
);

Clicking our submit button should now submit the order, and you should see a new order appear in your Swell store. Congrats! You’re now on your way to creating your own custom checkout. From here, you can continue building out your checkout flow, adding extra steps like shipping and billing info where necessary.