DEV Community

Cover image for Creating a React Context Provider for Paddle JS with Typescript
Tom Holder
Tom Holder

Posted on • Originally published at boardshape.com

Creating a React Context Provider for Paddle JS with Typescript

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:

A better DX experience for Paddle is in the works

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 {}
Enter fullscreen mode Exit fullscreen mode

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 }

Enter fullscreen mode Exit fullscreen mode

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 }

Enter fullscreen mode Exit fullscreen mode

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>)
Enter fullscreen mode Exit fullscreen mode

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
}
Enter fullscreen mode Exit fullscreen mode

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.****

Top comments (0)