Step 1: Set Up Your Supabase Project
Go to supabase.com and create a new project. Choose a region close to your users (EU West for European apps, US East for American apps).
Create your first table in the Supabase Table Editor. For a task management app: CREATE TABLE tasks ( id bigserial PRIMARY KEY, title text NOT NULL, status text DEFAULT 'todo', user_id uuid REFERENCES auth.users(id), created_at timestamptz DEFAULT now() );
Enable Row-Level Security: ALTER TABLE tasks ENABLE ROW LEVEL SECURITY; CREATE POLICY "Users can only see their own tasks" ON tasks FOR ALL USING (auth.uid() = user_id);
Step 2: Connect Supabase to WeWeb
In WeWeb, go to Plugins → Data Sources → Supabase. Enter your Supabase URL and anon key (both in your Supabase project settings → API).
WeWeb will automatically detect your tables and generate typed data sources. You'll see a "tasks" collection in your WeWeb data panel, ready to bind to UI components.
Step 3: Build the Authentication Flow
In WeWeb, create a login page with an email and password input, connected to WeWeb's Supabase Auth plugin. Use the "Sign in with email/password" action on the login button.
Protect your app pages with WeWeb's page access conditions: "Redirect to /login if user is not authenticated." This client-side check + Supabase RLS gives you defense in depth.
Step 4: Bind Data to UI Components
Create a list component in WeWeb and bind it to your tasks collection. Set the collection filter to automatically pass the current user's JWT, WeWeb's Supabase plugin handles this automatically.
For each list item, bind the task title to a text element, and the status to a badge with conditional color styling: green for "done", blue for "in-progress", gray for "todo".
Step 5: Create, Update, and Delete Operations
WeWeb's Supabase plugin exposes insert, update, and delete actions. Wire them to: - A "New task" button that opens a modal form, calls insert on submit - An inline status dropdown that calls update on change - A delete button that calls delete after a confirmation dialog
All operations respect RLS, users can only modify their own rows.
Step 6: Real-Time Updates
Enable Supabase Realtime in your project settings. In WeWeb, add a Realtime subscription to your tasks collection. Now when any task changes (from any device or tab), your WeWeb UI updates automatically.
This is powerful for collaborative tools, you get live collaboration with zero custom WebSocket code.
Step 7: Deploy to Production
In WeWeb, click "Publish" → enter your custom domain. WeWeb deploys your app to a global CDN with HTTPS. Update your domain's DNS records to point to WeWeb's CDN edge.
Your Supabase database is already production-hosted. You now have a complete, scalable full-stack app, with a custom domain, auth, real-time data, and zero server management.
Setting Up RLS Step by Step
Row-Level Security is the feature that makes Supabase safe for multi-user apps. Without it, a bug in your frontend could expose every user's data to every other user. Enable it on every table before you connect it to WeWeb.
The process: open Supabase Dashboard → Database → Tables → click your table → toggle Enable RLS. Then click Policies → New Policy. Choose a template ("Allow users to read their own data" is a good starting point) and customise the USING clause. The USING expression is evaluated for every row, only rows where the expression returns true are returned to the client.
For a tasks table with a user_id column: the SELECT policy USING expression is auth.uid() = user_id. For INSERT, use the WITH CHECK expression: auth.uid() = user_id. This ensures users can only insert rows where they own the user_id, they cannot impersonate another user even if they manipulate the request payload.
Connecting WeWeb to Supabase: Deep Dive
The Supabase plugin in WeWeb does more than you might expect from initial setup. Once connected, it exposes three types of data interactions: collections (fetched on page load or on demand), actions (insert, update, delete, upsert triggered by user events), and real-time subscriptions (live change streams from your database).
For collections, configure the fetch settings carefully: set the column select to only fetch columns you actually need (avoid SELECT * on wide tables), add filters to scope the query (e.g., status equals 'active'), and set the order and limit to paginate large datasets. A common mistake is fetching 1,000 rows on page load when you only display 20, this slows your app and wastes database connections.
For authentication, WeWeb's Supabase Auth plugin stores the user session in localStorage and automatically refreshes the JWT before expiry. All collections and actions created through the Supabase plugin automatically include the current user's JWT in the Authorization header. Your RLS policies receive this JWT via the auth.uid() function, the entire auth chain works without any manual token handling.
Dynamic Data Binding in WeWeb
WeWeb's binding system lets you connect any UI property to any variable, collection item, or formula. The pattern for dynamic lists: create a collection (e.g., tasks), drop a repeater component on the canvas, and bind the repeater's items property to tasks.data. Each child element inside the repeater has access to the current item via the loop variable.
For conditional display, showing a badge only when status equals 'overdue', hiding a button when the user is not an admin, use WeWeb's conditional visibility bindings. The condition can reference any variable, including the current user's role from your Supabase profile table.
Formulas in WeWeb use JavaScript syntax. To display a formatted date, bind the text to: new Date(item.created_at).toLocaleDateString('en-GB'). To compute a derived value from multiple fields, write the formula inline in the binding panel. This keeps your UI logic where it belongs, in the UI layer, rather than polluting your database or API with presentation concerns.
Form Submissions and Validation
Building forms in WeWeb follows a consistent pattern: create input components, bind each input to a local variable (use WeWeb's page variables for this), add validation conditions to the submit button, and trigger the Supabase insert or update action on successful submission.
For client-side validation, use WeWeb's action flow conditions: before calling the Supabase insert action, check that required fields are not empty, that email inputs match an email format, and that numeric inputs are within expected ranges. Display error messages by binding a text element's visibility to a validation error variable.
For server-side validation, use Xano as your API layer instead of calling Supabase directly from WeWeb. This lets you add business rules (e.g., checking subscription limits, verifying uniqueness constraints, running pricing calculations) before the data hits the database. For forms with complex validation requirements, the WeWeb → Xano → Supabase chain is always more robust than WeWeb → Supabase direct.