How to Connect Bolt.new to Supabase: Auth, Database & RLS Policies (Without Errors)

Building a polished React or Next.js frontend in Bolt.new takes less than five minutes. However, the moment you ask the AI to add persistent user authentication or save records to a database, things often break inside the browser-based WebContainer environment.

How to connect Bolt.new to Supabase with Auth and Row Level Security policies

Developers routinely run into three frustrating walls when wiring Bolt.new to Supabase: missing import.meta.env variables that crash the preview screen, infinite OAuth redirect loops, and the notorious PostgreSQL Error 42501 (new row violates row-level security policy for table).

In this step-by-step engineering guide, you will learn how to connect Bolt.new to Supabase cleanly—complete with copy-paste SQL schemas, production-ready Row Level Security (RLS) policies, a hardened TypeScript client configuration, and a master prompt that stops Bolt from accidentally overwriting your backend credentials.

New to Bolt.new? If you are still setting up your initial UI prototype, start with our foundational Bolt.new Tutorial: How to Build Full-Stack Web Apps Without Coding before wiring up your production database.

Architecture Overview: How Bolt.new Talks to Supabase

Unlike traditional local development, Bolt.new runs your Vite or Next.js application inside an in-browser StackBlitz WebContainer. Because there is no persistent local backend server, your frontend communicates directly with Supabase's PostgREST API and GoTrue Auth engine over HTTPS using your public anon key.

Because the anon key is exposed in the browser, Row Level Security (RLS) is mandatory. If RLS is enabled without explicit policies, every INSERT or SELECT request from Bolt.new will fail silently or throw a 401/403 error.

+-------------------------------------------------------------------------+
|                   BOLT.NEW WEBCONTAINER (Browser IDE)                   |
|                                                                         |
|  +-----------------------+          +--------------------------------+  |
|  |   .env Configuration  | -------> |  src/lib/supabaseClient.ts     |  |
|  |  VITE_SUPABASE_URL    |          |  createClient(url, anonKey)    |  |
|  |  VITE_SUPABASE_ANON   |          |  persistSession: true          |  |
|  +-----------------------+          +---------------+----------------+  |
+-----------------------------------------------------|-------------------+
                                                      |
                                        HTTPS + JWT Bearer Token
                                                      |
                                                      v
+-------------------------------------------------------------------------+
|                       SUPABASE CLOUD BACKEND                            |
|                                                                         |
|  +-----------------------+          +--------------------------------+  |
|  |   Supabase Auth       | -------> |  PostgreSQL + RLS Firewall     |  |
|  |   Issues JWT Session  |  auth.   |  Checks: auth.uid() = user_id  |  |
|  |   (Email / OAuth)     |  uid()   |  Allows: SELECT, INSERT, etc.  |  |
|  +-----------------------+          +--------------------------------+  |
+-------------------------------------------------------------------------+

Step 1: Create Your Supabase Project & Grab API Keys

While Bolt.new offers a one-click native Supabase integration, manually linking your project via environment variables gives you full ownership of your SQL migrations, Auth redirect URLs, and edge functions from day one.

  1. Log in to the Supabase Dashboard and click New Project.
  2. Enter a project name, generate a strong database password, and select the region closest to your target users.
  3. Once the database finishes provisioning (roughly 60 seconds), navigate to Project Settings (Gear Icon) → API.
  4. Copy two values to your clipboard:
    • Project URL: Looks like https://xyzcompany.supabase.co
    • Project API Keys (anon / public): A long JWT string starting with eyJhbGci...

Security Warning: Never paste your service_role secret key into Bolt.new. The service_role key bypasses all Row Level Security policies and will expose your entire database in the browser bundle.


Step 2: Configure .env and Initialize the Supabase Client in Bolt.new

By default, Bolt.new scaffolds React applications using Vite. In Vite, environment variables are only exposed to your client-side code if they are prefixed with VITE_. Using NEXT_PUBLIC_ or raw SUPABASE_URL inside a Vite WebContainer will evaluate to undefined and crash your app.

1. Create Your .env File in Bolt's File Tree

Open the Code tab in Bolt.new, right-click the root directory, create a file named .env, and paste the following structure:

VITE_SUPABASE_URL=https://your-project-id.supabase.co
VITE_SUPABASE_ANON_KEY=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.your-anon-key

2. Install @supabase/supabase-js and Create the Singleton Client

In the Bolt.new terminal (or via prompt), ensure @supabase/supabase-js is installed, then create src/lib/supabaseClient.ts with explicit validation so missing keys throw a clear developer warning instead of a blank white screen:

import { createClient } from '@supabase/supabase-js';

const supabaseUrl = import.meta.env.VITE_SUPABASE_URL;
const supabaseAnonKey = import.meta.env.VITE_SUPABASE_ANON_KEY;

if (!supabaseUrl || !supabaseAnonKey) {
  throw new Error(
    'Missing Supabase environment variables! Check VITE_SUPABASE_URL and VITE_SUPABASE_ANON_KEY in your .env file.'
  );
}

export const supabase = createClient(supabaseUrl, supabaseAnonKey, {
  auth: {
    persistSession: true,
    autoRefreshToken: true,
    detectSessionInUrl: true,
  },
});
Comparing Full-Stack Builders? Some developers prefer native Supabase wrappers. Read our deep-dive Lovable vs Bolt.new Comparison (2026) to see how both platforms handle backend state and database migrations.

Step 3: Run This Idempotent SQL Schema & RLS Policy Script in Supabase

The #1 mistake developers make is asking Bolt.new to "create the database tables." Bolt cannot execute DDL (Data Definition Language) queries through the standard anon client unless you use its managed connector, and even then, AI-generated policies often cause infinite recursion.

Instead, open your Supabase Dashboard → SQL Editor → New Query, paste the production-ready SQL blueprint below, and click Run. This sets up a profiles table, an automatic trigger when a user signs up, a sample tasks table, and strict auth.uid() RLS policies.

-- 1. Enable UUID extension
create extension if not exists "uuid-ossp";

-- 2. Create a public Profiles table linked to auth.users
create table if not exists public.profiles (
  id uuid references auth.users on delete cascade primary key,
  email text unique not null,
  full_name text,
  created_at timestamptz default now() not null
);

-- 3. Enable RLS on Profiles
alter table public.profiles enable row level security;

-- 4. Profiles RLS Policies
create policy "Users can view their own profile"
  on public.profiles for select
  to authenticated
  using (auth.uid() = id);

create policy "Users can update their own profile"
  on public.profiles for update
  to authenticated
  using (auth.uid() = id)
  with check (auth.uid() = id);

-- 5. Auto-create Profile row when a new user signs up via Bolt.new
create or replace function public.handle_new_user()
returns trigger
language plpgsql
security definer set search_path = public
as $$ begin insert into public.profiles (id, email, full_name) values ( new.id, new.email, coalesce(new.raw_user_meta_data->>'full_name', '') ); return new; end; $$;

drop trigger if exists on_auth_user_created on auth.users;
create trigger on_auth_user_created
  after insert on auth.users
  for each row execute procedure public.handle_new_user();

-- 6. Create an App Data table (e.g., user_tasks) with automatic auth.uid() default
create table if not exists public.user_tasks (
  id uuid default gen_random_uuid() primary key,
  user_id uuid references auth.users on delete cascade default auth.uid() not null,
  title text not null,
  is_completed boolean default false not null,
  created_at timestamptz default now() not null
);

-- 7. Enable RLS on user_tasks
alter table public.user_tasks enable row level security;

-- 8. Full CRUD Policies for user_tasks (Prevents Error 42501)
create policy "Users can select their own tasks"
  on public.user_tasks for select
  to authenticated
  using (auth.uid() = user_id);

create policy "Users can insert their own tasks"
  on public.user_tasks for insert
  to authenticated
  with check (auth.uid() = user_id);

create policy "Users can update their own tasks"
  on public.user_tasks for update
  to authenticated
  using (auth.uid() = user_id)
  with check (auth.uid() = user_id);

create policy "Users can delete their own tasks"
  on public.user_tasks for delete
  to authenticated
  using (auth.uid() = user_id);

Why default auth.uid() Saves Hours of Debugging

Notice line 48 in the SQL script above: user_id uuid ... default auth.uid() not null. Frequently, Bolt.new generates frontend INSERT statements that forget to pass the logged-in user's user_id in the payload. By setting default auth.uid() directly in PostgreSQL, Supabase automatically populates user_id from the user's JWT token—eliminating 90% of RLS violation errors immediately.


Step 4: Disable Email Confirmation for Instant Testing in Bolt.new

When testing signup flows inside Bolt.new's embedded preview browser, email verification links often fail because the confirmation URL redirects to http://localhost:3000 outside of the StackBlitz WebContainer iframe.

  1. In your Supabase Dashboard, go to Authentication → Providers → Email.
  2. Toggle OFF the setting labeled Confirm email during development and click Save.
  3. If you need OAuth or magic links later, navigate to Authentication → URL Configuration and add your Bolt.new preview URL (e.g., https://*.local-credentialless.webcontainer-api.io/**) and your production Vercel domain under Redirect URLs.

Step 5: The Master Prompt to Wire Up Auth & CRUD in Bolt.new

Never give Bolt a vague instruction like "Connect this to Supabase." Vague prompts cause the AI to rewrite your .env file, invent non-existent column names, or use deprecated Supabase Auth Helpers. Copy and paste this exact structured prompt into Bolt.new:

I have already configured `.env` with `VITE_SUPABASE_URL` and `VITE_SUPABASE_ANON_KEY`, and initialized the singleton client in `src/lib/supabaseClient.ts`. DO NOT overwrite `.env` or create a duplicate Supabase client.

My Supabase database has two tables with Row Level Security (RLS) enabled:
1. `public.profiles` (columns: `id`, `email`, `full_name`, `created_at`)
2. `public.user_tasks` (columns: `id`, `user_id` [defaults to auth.uid()], `title`, `is_completed`, `created_at`)

Please implement the following:
1. Create an `AuthContext` using `supabase.auth.getSession()` and `supabase.auth.onAuthStateChange()` to track the active user session.
2. Build a clean Login / Sign Up form using `supabase.auth.signInWithPassword` and `supabase.auth.signUp`.
3. Replace any mock state in the dashboard with live CRUD calls to `public.user_tasks` (`select('*')`, `insert([{ title }])`, `update({ is_completed })`, and `delete().eq('id', taskId)`).
4. Display specific error messages from `error.message` in a toast notification if any query fails.
Hitting Bolt.new Token Limits on Complex SQL Logic? Don't burn your daily tokens fighting edge-case bugs. Follow our guide on How to Transfer Code from Bolt.new to Claude to debug complex database hooks outside the WebContainer.

Troubleshooting Matrix: Fixing Common Bolt.new + Supabase Errors

If your app throws an error in the browser console or Supabase Postgres logs, locate the exact code in the table below for the immediate fix:

Error Code / Symptom Root Cause in Bolt.new Exact Engineering Fix
Error 42501
new row violates row-level security policy
Missing WITH CHECK clause on your INSERT policy, or frontend didn't pass user_id. Set column default to auth.uid() and verify policy includes with check (auth.uid() = user_id).
Error PGRST204
Could not find column in schema cache
Bolt's frontend code references a camelCase column (e.g., isCompleted) instead of snake_case (is_completed). Run NOTIFY pgrst, 'reload schema'; in SQL Editor and align TypeScript interface names with Postgres snake_case.
Error 42P17
infinite recursion detected in policy
AI wrote an RLS policy on profiles that queries profiles inside its own USING check. Drop the recursive policy and replace it with a direct using (auth.uid() = id) check.
Blank White Preview
supabaseUrl is required
Environment variables lack the VITE_ prefix or Bolt's Vite dev server wasn't restarted after editing .env. Rename keys to VITE_SUPABASE_URL and run npm run dev in the WebContainer terminal to reload env vars.
401 Unauthorized on every query after login Third-party cookies/localStorage blocked inside Bolt's preview pane. Click the "Open Preview in New Tab" icon in the top-right of Bolt.new so localStorage persists the JWT session.

Step 6: Moving from Bolt.new to Production (GitHub & Vercel)

Once your authentication and database operations work smoothly inside Bolt.new, remember that your .env file is excluded from Git commits via .gitignore for security.

When you deploy your application to a live domain, you must copy both VITE_SUPABASE_URL and VITE_SUPABASE_ANON_KEY into your hosting provider's Environment Variables dashboard, and add your live production domain to Supabase's Site URL under Authentication settings.

Ready to Ship Your App Live? Follow our complete deployment blueprint: How to Export Bolt.new to GitHub and Deploy to Vercel (Step-by-Step).

Frequently Asked Questions (FAQ)

Why does Bolt.new say "new row violates row-level security policy" when inserting data?

PostgreSQL throws Error 42501 when RLS is enabled on a table, but either no FOR INSERT policy exists for the authenticated role, or the row you are inserting has a user_id that does not match the currently logged-in user's auth.uid(). Setting your user_id column default to auth.uid() and adding WITH CHECK (auth.uid() = user_id) resolves this immediately.

Should I use Bolt.new's native Supabase button or connect manually via .env?

The native button is convenient for quick prototypes, but connecting manually via VITE_SUPABASE_URL and VITE_SUPABASE_ANON_KEY is strongly recommended for production projects. Manual setup prevents Bolt from running unreviewed SQL migrations that can accidentally drop columns or introduce recursive RLS policies.

Can I use Prisma or Drizzle ORM inside Bolt.new with Supabase?

While you can define schemas with Prisma or Drizzle, running direct TCP database connections inside Bolt.new's browser-based WebContainer is unreliable due to network sandboxing. Using the official @supabase/supabase-js client over HTTPS (PostgREST) is faster, works natively in the browser, and respects Row Level Security out of the box.

What if I want to migrate my Bolt + Supabase app to a local AI IDE later?

Because your database schema and RLS policies live in Supabase Cloud and your frontend uses standard Vite environment variables, you can export your codebase at any time and open it in Cursor AI or VS Code. See our Bolt.new vs v0 vs Cursor vs Claude Artifacts comparison for the best local transition workflow.

Comments (0)