I was excited to start the integration of Paddle with BoardShape. The fact Paddle is a MoR (Merchant of Record) should make life substantially easier from an admin perspective. Having previously worked with Stripe though and their amazing DX I found the Paddle process for JS somewhat lacking. Link this file, do some stuff.
As BoardShape (Board Management Software for Startups) is written in NextJS and React using TypeScript I wanted a better way to work with it. I found a few examples but none of them were really terribly React friendly.
I know Paddle are working on an improved version:
Until then though, the cleanest way to consume Paddle would seem to be a context provider.
Here's what you need to do to make it work:
Add in to a types.d.ts somewhere in your project:
declare global { var Paddle: any } export {}
There's a few examples out there (such as https://stackoverflow.com/questions/65842582/how-to-integrate-paddle-with-nextjs) where people have provided stronger types, however, unless there is something official from Paddle I don't massively see the point in adding it so I've just typed it to any.
Now, add your context provider PaddleProvider.tsx
'use client' import { createContext, useState } from 'react' import Script from 'next/script' type PaddleContext = any const PaddleContext = createContext<PaddleContext>(null) const PaddleProvider = ({ children }: { children: React.ReactNode }) => { const [paddle, setPaddle] = useState(null) return ( <PaddleContext.Provider value={paddle}> <> <Script src="https://cdn.paddle.com/paddle/paddle.js" onLoad={() => { if (process.env.NEXT_PUBLIC_PADDLE_SANDBOX) { Paddle.Environment.set('sandbox') } Paddle.Setup({ vendor: Number(process.env.NEXT_PUBLIC_PADDLE_VENDOR_ID), }) setPaddle(Paddle) }} /> {children} </> </PaddleContext.Provider> ) } export { PaddleProvider, PaddleContext }
Note the two env vars NEXT_PUBLIC_PADDLE_SANDBOX and NEXT_PUBLIC_PADDLE_VENDOR_ID that you need to add to your .env file.
Also note, we set the initial state to null, not undefined. The reason will become apparent in the context consumer.
Next, add a ContextConsumer.tsx
import { useContext } from 'react' import { PaddleContext } from '@components/Paddle/PaddleProvider' // context consumer hook const usePaddleContext = () => { // get the context const context = useContext(PaddleContext) // if `undefined`, throw an error if (context === undefined) { throw new Error('usePaddleContext was used outside of its Provider') } return context } export { usePaddleContext }
Next, in our page where we use Paddle, wrap everything in the provider (this is only a snippet):
import { PaddleProvider } from '@components/Paddle/PaddleProvider' ... return (<PaddleProvider> YOUR COMPONENTS </PaddleProvider>)
I only wrap paged where I know I need paddle so you may not want to wrap your entire app with this provider as it will cause the remote JS to load.
Now, wherever you need Paddle:
import { usePaddleContext } from '@components/Paddle/PaddleConsumer' ... const paddle = usePaddleContext() if (paddle) { //It's loaded do some stuff }
That's it. I look forward to this post being made obsolete by Paddle hopefully releasing a React/Typescript friendly library that can just be installed in to your project with a package manager.