"Add login with Supabase" might be the most-typed prompt in vibe coding, and Supabase earns it: the auth is genuinely three lines. But those three lines are the easy 10%. The other 90% — which key the browser sees, what happens when the password is wrong, whether a new user has to confirm their email — is decided by your prompt, or, if you don't decide, by the model's best guess.
We build this site the same way, so here's the honest version: the same request, given three ways to Claude via Claude Code, with the real code it produced each time. Watch what gets pinned down as the prompt gets more specific.
The same request, three ways
"Add login with Supabase", three ways
Each prompt was given to Claude, via Claude Code (26 Sep 2026) on its own. Outputs pasted verbatim, trimmed only with ….
add login with supabase
import { createClient } from '@supabase/supabase-js';
const supabase = createClient(SUPABASE_URL, SUPABASE_KEY);
async function login(email, password) {
const { data } = await supabase.auth.signInWithPassword({ email, password });
return data;
}worseIt logs in, but SUPABASE_KEY is undefined, the error is thrown away, and there's no sign-up, confirmation or redirect. A demo, not a feature.
add email + password login with Supabase: read the keys from env, show errors, redirect to /dashboard on success, and include a sign-up option
import { createClient } from '@supabase/supabase-js';
const supabase = createClient(
process.env.NEXT_PUBLIC_SUPABASE_URL,
process.env.NEXT_PUBLIC_SUPABASE_ANON_KEY,
);
async function login(email, password) {
const { error } = await supabase.auth.signInWithPassword({ email, password });
if (error) return showError(error.message);
window.location.href = '/dashboard';
}
async function signUp(email, password) {
const { error } = await supabase.auth.signUp({ email, password });
if (error) return showError(error.message);
showMessage('Check your email to confirm your account.');
}it dependsNow it reads the anon key from an env var, surfaces errors, redirects on success, and offers sign-up with email confirmation.
same login, but: only ever use the anon key in the browser (never the service key), add a loading state that disables the button while submitting, and handle the 'user already registered' case
// Browser uses the ANON key only. The service key never leaves the server.
import { createClient } from '@supabase/supabase-js';
const supabase = createClient(
process.env.NEXT_PUBLIC_SUPABASE_URL,
process.env.NEXT_PUBLIC_SUPABASE_ANON_KEY,
);
async function submit(mode, email, password, setLoading) {
setLoading(true);
try {
const fn = mode === 'signup' ? 'signUp' : 'signInWithPassword';
const { error } = await supabase.auth[fn]({ email, password });
if (error) {
if (error.message.includes('already registered'))
return showError('That email is already in use — try logging in.');
return showError(error.message);
}
window.location.href = mode === 'signup' ? '/check-email' : '/dashboard';
} finally {
setLoading(false);
}
}betterThe comment on line 1 is the whole security point: the browser gets the anon key, never the service key. Plus a loading guard and the 'already registered' case a real user will hit.
What changed: Supabase makes login three lines. The spec is everything else: which key, what errors, what happens on success, and the edge cases a stranger will hit before you do.
The jump from vague to constrained didn't need a smarter model. It needed you to say the parts a careful teammate would ask about: the key, the failure path, the loading state, the duplicate-email case. Each sentence you add is one fewer thing the AI decides for you.




