Dynamically set an Account ID in Stripe using loadStripe

Last Updated:
Stripe logo on colourful gradient next to payment app mockup

This post contain affiliate links to Udemy courses, meaning when you click the links and make a purchase, I receive a small commission. I only recommend courses that I believe support the content, and it helps maintain the site.

I recently had to integrate Stripe into a React project. It seemed like it would be fairly simple.

The Stripe React documentation seemed really well put together, and looked like the components just needed plugging in and styling. Perfect!

Unfortunately that was not the case for my particular requirement.

The Brief: Dynamically set a Stripe account ID

An API call fetches a particular Stripe account ID depending on the business that will be paid. This account ID will be passed to the Stripe package.

This means that that account ID is not available immediately on page load. And is subject to change.

The Problem: Limitations with loadStripe in React

The Stripe documentation outlines many different payment methods. But all examples come back to the same implementation requirement.

Make sure to call loadStripe outside of a component’s render to avoid recreating the `Stripe` object on every render.

That isn’t the best news, as the account ID will be passed in as a prop and dynamically added to the loadStripe function. It does not exist when the page is first loaded.

When I tried it within the component render, I received an error:

Unsupported prop change on Elements: You cannot change the `stripe` prop after setting it.

If you came across this, you probably found this thread on StackOverflow: https://stackoverflow.com/questions/64693509/unsupported-prop-change-on-elements-you-cannot-change-the-stripe-prop-after-s

However, this article doesn’t solve our particular use case.

So, it isn’t just a case of avoiding rerendering. It simply cannot be done like this.

The Solution: How to use useEffect and useState with Stripe in React

There needs to be a way to pass an account ID into a component as a prop. Then pass this to the Stripe React loadStripe function dynamically. And only run this function once. This will then pass the Stripe object to the Elements and CardElement component.

So how can this be done?

The example below is a very simple implementation that will allow you to pick apart and use as you need.

A lot of the examples show using loadStripe direct from the @stripe/stripe-js package. To make it work the way we need, loadStripe needs to be imported from the @stripe/stripe-js/pure module.

import React, { useEffect, useState } from "react";
import { Elements } from "@stripe/react-stripe-js";
import { loadStripe } from "@stripe/stripe-js/pure";

Then we need to get the Stripe object from loadStripe asynchronous and save it into the state. For this use a useEffect function.

const [stripeObject, setStripeObject] = useState(null);

  // This function will re-run if the accountId prop changes.
  useEffect(() => {
    const fetchStripeObject = async () => {
      // If there is no accountId, do not run the loadStripe function.
      if (accountId) {
        const res = await loadStripe(
          YOUR_PUBLISHABLE_KEY,
          {
            stripeAccount: accountId
          }
        );
        // When we have got the Stripe object, pass it into our useState.
        setStripeObject(res);
      }
    };
    fetchStripeObject();
  }, [accountId]);

In this code snippet a useState function is being set which gives the ability to save the Stripe object once received.

A useEffect function is then used to listen to the accountId prop. When this prop changes the useEffect function will rerender.

However, the loadStripe function only wants to fire when there is an accountId to use, so this is wrapped in an if statement.

Once the loadStripe function returns the Stripe object, it is saved into the stripeObject to be used later.

This piece of code can then be rounded off by adding a conditional render. If there is no Stripe object – show a loading screen. Otherwise set the Element component.

// If no Stripe object, do not render the Stripe Element.
if (!stripeObject) {
   return <p>Loading...</p>;
}

// Once we have the Stripe object, load everything.
return <Elements stripe={stripeObject}>{children}</Elements>;

This wrapper can then be used around the payment components provided by Stripe within the @stripe/react-stripe-js package. The whole wrapper would look like this:

import React, { useEffect, useState } from "react";
import { Elements } from "@stripe/react-stripe-js";
import { loadStripe } from "@stripe/stripe-js/pure";

function StripeWrapper({ accountId, children }) {
  const [stripeObject, setStripeObject] = useState(null);

  // This function will re-run if the accountId prop changes.
  useEffect(() => {
    const fetchStripeObject = async () => {
      // If there is no accountId, do not run the loadStripe function.
      if (accountId) {
        const res = await loadStripe(
          "YOUR_ACCOUNT_ID,
          {
            stripeAccount: accountId
          }
        );
        // When we have got the Stripe object, pass it into our useState.
        setStripeObject(res);
      }
    };
    fetchStripeObject();
  }, [accountId]);

  // If no Stripe object, do not render the Stripe Element.
  if (!stripeObject) {
    return <p>Loading...</p>;
  }

  // Once we have the Stripe object, load everything.
  return <Elements stripe={stripeObject}>{children}</Elements>;
}

export default StripeWrapper;

For a full working example of this, take a look at the repo I put together. A quick clone will help show it fully implemented.

https://github.com/robmarshall/stripe-react-dynamic-account-id

 

I hope this helped get you moving. Let me know if I can be anymore help by contacting me on twitter @robertmars

Related Posts

Helpful Bits Straight Into Your Inbox

Subscribe to the newsletter for insights and helpful pieces on React, Gatsby, Next JS, Headless WordPress, and Jest testing.