SaaS Starter Template for Lovable
Multi-tenant workspaces, three roles, and a working invite flow, with the row-level security that makes tenancy enforced rather than cosmetic.
Stack: React + Vite, TypeScript, Tailwind CSS, shadcn/ui, Supabase
What is in the download
- A 400-word build prompt with numbered priority, an explicit build order and safeguards
- A Supabase schema: profiles, workspaces, workspace_members and workspace_invites
- Row-level security on every table, deny by default, with owner, admin and member roles
- Two SECURITY DEFINER helpers that avoid the recursive-policy trap on a membership table
- An invite acceptance function, because the invitee cannot select the row they are accepting
- Six verification queries that prove a member cannot promote themselves to owner
What it is not
- Not a pre-built app you clone and run. There is no repository and no node_modules.
- Not a design file or a theme. Lovable generates the interface from the prompt.
- Not a substitute for reading the SQL. You are running it against your own database.
Setup order
Running these out of order is the usual reason a template appears broken. The schema comes before the prompt, because the prompt references the table names it creates.
Run schema.sql in the Supabase SQL editor
Before prompting Lovable. The prompt references these table names, so building first means rework.
Run verify.sql as an ordinary signed-in user
Not as service_role, which bypasses every policy and will make a broken schema look correct.
Paste prompt.md into Lovable
Unedited on the first pass, so you can see what the structure produces before changing it.
Replace the audience line
The Context section names a small team adopting a tool. Changing that one line changes which features Lovable prioritises more than anything else in the prompt.
Things that catch people out
- The trigger that makes a workspace creator an owner is load-bearing. Without it the creator cannot read the workspace they just made, because the read policy requires membership. This is the most common way this schema appears broken when it is not.
- The invite table has no anonymous read policy on purpose. Anyone who can select from it can enumerate pending invite tokens for every workspace.
- Lovable will sometimes generate a client-side role check and nothing else. The policies here mean a missing UI check is a cosmetic bug rather than a data breach.
prompt.md
DownloadThe build prompt
# Context
Build a multi-tenant SaaS application where users belong to workspaces and a workspace has
its own members, roles and plan state. The target user is a small team adopting a tool
together, so the first thing that must work is inviting a colleague and having them land in
the same workspace.
## Core Features (Priority Order)
1. Email and password authentication with session persistence across refresh
2. Workspace creation on first sign-in, with the creator as owner
3. Workspace member list with three roles: owner, admin, member
4. Invite by email, producing a tokenised link that joins the invitee to the same workspace
5. Role-gated UI: only owner and admin can invite, change roles or remove members
6. Plan state visible in the app (free or paid) with an upgrade call to action
## Visual Style
- Professional SaaS aesthetic, spacing on a 4px grid, 8px corner radius
- shadcn/ui: Card for panels, Table for the member list, Badge for role and plan,
Avatar for members, Dialog for the invite flow, Button for actions
- One accent colour used only for primary actions and active navigation
## Technical Requirements
- Breakpoints sm 640px, md 768px, lg 1024px, mobile first
- Loading skeletons for the member list, not spinners
- Empty state for a workspace with one member, prompting the first invite
- Error state for an expired or already-used invite token
- Form validation with inline messages, never an alert
- Dark mode via Tailwind's dark: prefix
## Implementation Strategy
Build in this order and do not reorder it:
1. Auth and the protected route wrapper
2. The role-checking helper, before any UI that depends on a role
3. Workspace and member reads against the schema in schema.sql
4. The invite write path, including the expired-token case
5. Plan display last, because it depends on workspace state existing
## Safe-Guard Instructions
- Enforce entitlement in application code as well as in the database. Do not assume a
database function named like a permission check actually enforces anything.
- Never select from workspace_members without a workspace filter.
- Treat role as server-derived. Do not accept a role from client state.
- Do not weaken any row-level security policy from schema.sql to make a query work. If a
query is denied, the query is wrong.
## Growth Features (Required for Distribution)
- **Product-led Growth:** the invite is the growth loop, so it must work before anything
else is polished. Make the invite link shareable outside email, and show the inviter
when an invite is accepted.schema.sql
In the bundleTables, policies and functions
-- SaaS starter schema for Lovable + Supabase
-- Reviewed row-level security. Read the comments before running this.
--
-- The recursive-policy trap: a policy on workspace_members that itself queries
-- workspace_members causes infinite recursion. The is_workspace_member() function below is
-- SECURITY DEFINER specifically to break that cycle. Do not inline it into a policy.
create type public.workspace_role as enum ('owner', 'admin', 'member');
create table public.profiles (
id uuid primary key references auth.users (id) on delete cascade,
full_name text,The rest of schema.sql, with every policy written and its denials verified, is in The Complete Lovable Bundle.
Get the Bundleverify.sql
In the bundlePolicy verification queries
-- Verification queries. Run these as an ordinary signed-in user, not as service_role.
-- Every one of them must behave as described or a policy is wrong.
-- 1. Should return only workspaces you are a member of, never every workspace.
select id, name, plan from public.workspaces;
-- 2. Should return zero rows for a workspace you do not belong to.
-- Substitute a workspace id you are NOT a member of.
select * from public.workspace_members
where workspace_id = '00000000-0000-0000-0000-000000000000';
-- 3. Should FAIL for a member (non-admin). If it succeeds, the write policy is too loose.The rest of verify.sql, with every policy written and its denials verified, is in The Complete Lovable Bundle.
Get the BundleNext
If you want a prompt rather than a whole starting point, the prompt library has 101 of them across six categories. If you are watching what this costs to build, how Lovable credits work explains why a vague prompt against a large project is expensive twice over.