Why Webflow + Supabase Is a Power Combo
Webflow gives you pixel-perfect design control and native hosting. Supabase gives you a Postgres database, instant REST and GraphQL APIs, authentication, storage, and real-time subscriptions — all open-source. Together they let a small team ship products that used to require a full backend squad. The key insight is that Supabase exposes every feature through client-side JavaScript libraries, which means you can call them directly from Webflow's custom code embed or a lightweight script tag.
Architecture Overview
The integration follows a clean three-layer pattern. The presentation layer lives entirely in Webflow — pages, CMS collections, interactions, and responsive layouts. The data layer is a Supabase project with tables, Row Level Security (RLS) policies, and Edge Functions for any server-side logic you need. The glue layer is a small JavaScript module loaded via a script tag in Webflow's project-level custom code settings. This module initializes the Supabase client, listens for DOM events (form submissions, button clicks, page loads), and reads from or writes to the database accordingly.
// Initialize Supabase in Webflow's <head> custom code
import { createClient } from "https://esm.sh/@supabase/supabase-js@2";
const supabase = createClient(
"https://your-project.supabase.co",
"your-anon-key" // safe to expose — RLS protects data
);
// Example: fetch and render a list of testimonials
async function loadTestimonials() {
const { data, error } = await supabase
.from("testimonials")
.select("name, quote, company, avatar_url")
.eq("approved", true)
.order("created_at", { ascending: false })
.limit(6);
if (error) { console.error(error); return; }
const container = document.querySelector("[data-element='testimonials']");
container.innerHTML = data.map(t => `
<div class="testimonial-card">
<img src="${t.avatar_url}" alt="${t.name}" />
<blockquote>${t.quote}</blockquote>
<cite>${t.name}, ${t.company}</cite>
</div>
`).join("");
}
document.addEventListener("DOMContentLoaded", loadTestimonials);Setting Up Authentication
Supabase Auth supports email/password, magic links, OAuth providers (Google, GitHub, Apple), and phone OTP out of the box. In a Webflow context the easiest path is magic links — you collect the user's email through a Webflow form, call supabase.auth.signInWithOtp(), and Supabase sends the link. When the user clicks it, they land back on your Webflow site with a session token in the URL hash. Your glue script detects the token on page load, stores the session, and toggles UI elements between logged-in and logged-out states using Webflow interactions or simple class swaps.
// Magic-link sign-in wired to a Webflow form
const form = document.getElementById("login-form");
form.addEventListener("submit", async (e) => {
e.preventDefault();
const email = form.querySelector("[name='email']").value;
const { error } = await supabase.auth.signInWithOtp({
email,
options: { emailRedirectTo: window.location.origin + "/dashboard" },
});
if (error) alert(error.message);
else showMessage("Check your inbox for a login link.");
});
// On page load, check for an active session
const { data: { session } } = await supabase.auth.getSession();
if (session) {
document.body.classList.add("is-logged-in");
}Row Level Security: The Backend You Don't Have to Build
Because the Supabase client runs in the browser, you must assume the anon key is public. Row Level Security is what keeps data safe. RLS policies are SQL rules attached to each table that determine which rows a given user can SELECT, INSERT, UPDATE, or DELETE. For example, a policy like 'Users can read only their own orders' translates to: using (auth.uid() = user_id). With RLS enabled, even if someone inspects your JavaScript, they cannot read or modify another user's data.
-- Enable RLS on the orders table
ALTER TABLE orders ENABLE ROW LEVEL SECURITY;
-- Allow users to read their own orders
CREATE POLICY "Users read own orders"
ON orders FOR SELECT
USING (auth.uid() = user_id);
-- Allow users to insert their own orders
CREATE POLICY "Users insert own orders"
ON orders FOR INSERT
WITH CHECK (auth.uid() = user_id);Real-Time Subscriptions for Live Updates
One of the most impressive features of Supabase is its real-time engine built on top of Postgres logical replication. You can subscribe to INSERT, UPDATE, or DELETE events on any table and update your Webflow UI instantly. This is perfect for dashboards, notification feeds, live order trackers, and collaborative features. The subscription opens a WebSocket connection and pushes row-level changes as they happen, so there is no polling and no delay.
// Subscribe to new orders in real time
supabase
.channel("orders")
.on(
"postgres_changes",
{ event: "INSERT", schema: "public", table: "orders" },
(payload) => {
const order = payload.new;
prependOrderCard(order); // DOM helper to add a card
updateOrderCount(+1); // increment a counter in the UI
}
)
.subscribe();When to Use Supabase Edge Functions
Not everything should run in the browser. Use Supabase Edge Functions (Deno-based serverless functions) when you need to call a third-party API with a secret key, run complex validation logic, process payments through Stripe, or send transactional emails. Edge Functions are deployed with a single CLI command and can be called from your Webflow JavaScript like any fetch request. They keep your secrets out of the browser while still letting you avoid maintaining a full backend server.
Always enable RLS on every table that holds user data, even if you think the table is only accessed through Edge Functions. Defense in depth is the only sane policy when your database URL is technically public.
Want us to architect a Webflow + Supabase stack for your product?
Talk to our engineering team→
