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
- Supabase (opens in a new tab) Account
- Supabase project
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:
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
:
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:
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.
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!