Using Supabase with Payload CMS 3
This guide walks you through how to integrate Supabase with Payload CMS 3. Payload 3.0 is now fully Next.js native, installing directly into your Next.js application with both the admin panel and backend living right in your app folder.
Why Supabase + Payload CMS?
- Supabase provides a Postgres database, authentication, storage, and more.
- Payload CMS is now the first truly Next.js-native CMS with built-in server components support, local API capabilities, and seamless integration with Next.js features.
By using them together, you can:
- Leverage Supabase Authentication in Payload to manage your users and roles.
- Use Supabase's powerful PostgreSQL database - Payload 3.0 now has stable PostgreSQL support with Vercel PostgreSQL adapters.
- Take advantage of Supabase Storage for files or media while Payload handles structured content with its new bulk upload capabilities.
- Build high-performance applications using Payload's server components and local API for the fastest possible data access.
Key Features in Payload 3.0
-
Next.js Native Integration
- Install directly into any Next.js app
- Full admin panel and backend in your app folder
- Deploy anywhere: serverless on Vercel, containers, or your preferred platform
-
Database Flexibility
- Stable PostgreSQL support
- SQLite support
- Vercel PostgreSQL database adapters
- Powered by Drizzle ORM
-
Performance Optimizations
- Server components and server functions support
- Local API for direct database access
- Selective loading with new Select and Populate APIs
- React compiler integration for better performance
-
Enhanced Content Management
- Stable Lexical rich text editor with inline and block components
- Built-in jobs queue for task automation
- Bulk upload support
- Live Preview with server component support
Prerequisites
- Node.js (v16+)
- Next.js (App Router)
- Payload CMS v3 (installed via
create-payload-app
or added to existing Next.js project) - Supabase Project (set up in your Supabase dashboard)
1. Set Up Your Project
-
If starting from scratch, create a new Next.js project with Payload:
bash1pnpm create payload-appSelect the Website template to get started with server components and Tailwind CSS.
-
If adding to an existing Next.js project:
bash1pnpm add payload -
Set up your Supabase project:
- Go to app.supabase.com and create a new project
- Save your credentials:
- Project URL (often called
supabaseUrl
) - API Key (often called
supabaseKey
oranonKey
)
- Project URL (often called
2. Initialize Your Payload CMS 3 Project
If you haven't already installed Payload, you can do so by running:
1pnpm create payload-app my-payload-app
Follow the prompts to choose your configuration. Once it's set up:
1cd my-payload-app2pnpm install3pnpm run dev
By default, your local Payload CMS runs at http://localhost:3000/admin
.
3. Install the Supabase Client
Within your Payload project, install Supabase's JavaScript client:
1pnpm install @supabase/supabase-js
4. Connect Supabase and Payload
Create a helper module (e.g. supabaseClient.ts
or supabaseClient.js
) to configure and export a Supabase client you can reuse throughout your code:
1// supabaseClient.ts2import { createClient } from "@supabase/supabase-js";34const supabaseUrl = process.env.SUPABASE_URL || "";5const supabaseKey = process.env.SUPABASE_ANON_KEY || "";67export const supabase = createClient(supabaseUrl, supabaseKey);
Add the corresponding .env
variables:
1# .env2SUPABASE_URL=https://xyzcompany.supabase.co3SUPABASE_ANON_KEY=your-anon-key
Note: For production, you might want to use the service role key or a more secure environment variable. Keep your credentials safe!
5. Example: Using Supabase in Payload Hooks
A common scenario might be using Supabase whenever a new document is created or updated in Payload. For instance, you could create a user in Supabase when a new User
document is created in Payload.
1// payload.config.ts2import { buildConfig } from "payload/config";3import { supabase } from "./supabaseClient";45export default buildConfig({6 // ...rest of your Payload config7 collections: [8 {9 slug: "users",10 fields: [11 {12 name: "email",13 type: "email",14 required: true,15 },16 {17 name: "password",18 type: "text",19 required: true,20 },21 ],22 hooks: {23 afterChange: [24 async ({ operation, doc }) => {25 if (operation === "create") {26 // Create a user in Supabase27 const { email, password } = doc;28 const { error } = await supabase.auth.signUp({29 email,30 password,31 });3233 if (error) {34 console.error(35 "Error creating user in Supabase:",36 error.message37 );38 }39 }40 },41 ],42 },43 },44 ],45});
afterChange
is a Payload hook that triggers when a document is created or updated.operation === 'create'
ensures we only create the user in Supabase when the document is newly added.
6. Example: Using Supabase Storage in a Payload Collection
Suppose you want to leverage Supabase Storage to host images or files. You could store an image in Payload but also upload it to Supabase Storage in an afterChange
hook:
1// payload.config.ts2import { supabase } from "./supabaseClient";34export default buildConfig({5 collections: [6 {7 slug: "media",8 fields: [9 {10 name: "file",11 type: "upload",12 relationTo: "media-files",13 },14 ],15 hooks: {16 afterChange: [17 async ({ doc }) => {18 const fileData = doc?.file;19 if (fileData) {20 // This could be the file buffer, filename, etc.21 const fileBuffer = fileData?.buffer;22 const fileName = fileData?.filename || "payload-upload";2324 // Upload to Supabase Storage25 const { error } = await supabase.storage26 .from("your-bucket-name")27 .upload(`images/${fileName}`, fileBuffer);2829 if (error) {30 console.error(31 "Error uploading file to Supabase:",32 error.message33 );34 } else {35 console.log("File uploaded successfully to Supabase!");36 }37 }38 },39 ],40 },41 },42 ],43});
Adapt this to your actual file handling strategy. Payload's upload
fields can be configured to store locally or elsewhere before you sync it to Supabase.
7. Authentication Considerations
- Using Supabase Auth for the Payload Admin: Payload has its own auth, but you could offload user management to Supabase. In that case, you'd integrate or override Payload's auth logic with Supabase.
- JWT vs. Session: Payload uses JWT-based authentication. Supabase also uses JWT under the hood with additional helpers. Make sure you handle tokens securely if you're bridging the two systems.
8. Deployment Tips
- Self-Hosting: Both Supabase and Payload can be self-hosted. Ensure your environment variables (Supabase URL, keys, etc.) are correctly set in Docker, AWS, or wherever you host.
- Netlify / Vercel: If you host your front-end on Netlify or Vercel, keep your Payload server and Supabase project accessible from that environment.
- Security: Use secure tokens (like the service role key) only on the server side. Never expose them in public code.
Next Steps
- Explore Payload Plugins: You could build a dedicated Payload plugin to encapsulate your Supabase integration.
- Fine-Tune Hooks: Payload's hooks are very flexible—use them to integrate Supabase logic exactly where you need it.
- Consider Real-Time: Supabase provides real-time capabilities. You could push real-time updates from Supabase into your Payload front-end or use webhooks that update Payload content.
Conclusion
That's it! By combining Supabase and Payload CMS 3, you have a flexible, powerful system for data storage, user management, and file uploads. The best part is that both are straightforward once you get the hang of them.
Feel free to adapt the examples above to fit your exact use-case. Let me know if you have any questions—I'm happy to help. Keep building awesome stuff!
Happy coding!