Quickstart: Supabase

Quickstart with Supabase

Intro

Supabase is an open-source Firebase alternative.

This quickstart is a simple example showcasing how to use Supabase with Plasmo.

Prerequisites

Initialize a Plasmo project with Supabase

pnpm create plasmo --with-supabase

Set up Environment Variables

For Supabase to work, we'll need to define a URL and a KEY.

You can find these by finding them in your Supabase project dashboard:

Let's add them in an .env file:

.env
PLASMO_PUBLIC_SUPABASE_URL="CHANGE ME"
PLASMO_PUBLIC_SUPABASE_KEY="CHANGE ME"

Supabase Store

We need to initialize Supabase, so let's add a file called core/supabase.ts:

core/supabase.ts
import { createClient } from "@supabase/supabase-js"
 
import { Storage } from "@plasmohq/storage"
 
const storage = new Storage({
  area: "local"
})
 
export const supabase = createClient(
  process.env.PLASMO_PUBLIC_SUPABASE_URL,
  process.env.PLASMO_PUBLIC_SUPABASE_KEY,
  {
    auth: {
      storage,
      autoRefreshToken: true,
      persistSession: true,
      detectSessionInUrl: true
    }
  }
)

Adding Redirect URL

When a user signs up, they'll need to confirm their email. To do this, we need to add a redirect URL to our Supabase project.

First, we need to create a consistent ID for our extension for development. When you push to the different web stores, you'll get a different ID. To learn more about how all of this works, check out our blog post on creating consistent extension IDs (opens in a new tab)

Go to the Itero KeyPair Tool (opens in a new tab) to generate your consistent extension ID.

We can store the ID and public key in our .env file:

.env
CRX_ID="Replace with the value of CRX ID from Itero KeyPair tool"
CRX_KEY="Replace with the value of Public Key from Itero KeyPair tool"

Then, references the public key in your package.json under the manifest.key value:

"manifest": {
  "host_permissions": [
    "https://*/*"
  ],
  "key": "$CRX_KEY",
}

Now we need to make it so that the browser doesn't block access to our options page. To do this, we must add it to our web_accessible_resources in our manifest:

"web_accessible_resources": [
  {
    "resources": [
      "options.html"
    ],
    "matches": [
      "<all_urls>"
    ],
    "extension_ids": [
      "$CRX_ID"
    ]
  }
]

Head over to the Supabase console and click on the "Authentication" tab, and click URL Configuration.

Now add the following URL in your site URL as well as your redirect URL:

chrome-extension://<CRX_ID>/options.html

Replace CRX_ID with the generated extension ID given to you by Itero KeyPairs tool, or the actual ID given to you by the production web store.

Integrating with a React component

Now we can write code in our React components utilizing Supabase!

Here's an example of using Supabase in a React component for the extension's options page.

options.tsx
import type { Provider, User } from "@supabase/supabase-js"
import { useEffect, useState } from "react"
 
import { Storage } from "@plasmohq/storage"
import { useStorage } from "@plasmohq/storage/hook"
 
import { supabase } from "~core/supabase"
 
function IndexOptions() {
  const [user, setUser] = useStorage<User>({
    key: "user",
    instance: new Storage({
      area: "local"
    })
  })
 
  const [username, setUsername] = useState("")
  const [password, setPassword] = useState("")
 
  useEffect(() => {
    async function init() {
      const { data, error } = await supabase.auth.getSession()
 
      if (error) {
        console.error(error)
        return
      }
      if (!!data.session) {
        setUser(data.session.user)
      }
    }
 
    init()
  }, [])
 
  const handleEmailLogin = async (
    type: "LOGIN" | "SIGNUP",
    username: string,
    password: string
  ) => {
    try {
      const {
        error,
        data: { user }
      } =
        type === "LOGIN"
          ? await supabase.auth.signInWithPassword({
              email: username,
              password
            })
          : await supabase.auth.signUp({ email: username, password })
 
      if (error) {
        alert("Error with auth: " + error.message)
      } else if (!user) {
        alert("Signup successful, confirmation mail should be sent soon!")
      } else {
        setUser(user)
      }
    } catch (error) {
      console.log("error", error)
      alert(error.error_description || error)
    }
  }
 
  const handleOAuthLogin = async (provider: Provider, scopes = "email") => {
    await supabase.auth.signInWithOAuth({
      provider,
      options: {
        scopes,
        redirectTo: location.href
      }
    })
  }
 
  return (
    <main
      style={{
        display: "flex",
        justifyContent: "center",
        alignItems: "center",
        width: "100%",
        top: 240,
        position: "relative"
      }}>
      <div
        style={{
          display: "flex",
          flexDirection: "column",
          width: 240,
          justifyContent: "space-between",
          gap: 4.2
        }}>
        {user && (
          <>
            <h3>
              {user.email} - {user.id}
            </h3>
            <button
              onClick={() => {
                supabase.auth.signOut()
                setUser(null)
              }}>
              Logout
            </button>
          </>
        )}
        {!user && (
          <>
            <label>Email</label>
            <input
              type="text"
              placeholder="Your Username"
              value={username}
              onChange={(e) => setUsername(e.target.value)}
            />
            <label>Password</label>
            <input
              type="password"
              placeholder="Your password"
              value={password}
              onChange={(e) => setPassword(e.target.value)}
            />
 
            <button
              onClick={(e) => {
                handleEmailLogin("SIGNUP", username, password)
              }}>
              Sign up
            </button>
            <button
              onClick={(e) => {
                handleEmailLogin("LOGIN", username, password)
              }}>
              Login
            </button>
 
            <button
              onClick={(e) => {
                handleOAuthLogin("github")
              }}>
              Sign in with GitHub
            </button>
          </>
        )}
      </div>
    </main>
  )
}
 
export default IndexOptions

Full Example

To see a complete example, check out with-supabase (opens in a new tab) in our examples repo!