Skip to main content

Landing Page & Authentication

Before building any features, we're going to build authentication. This is the most important thing to get right early — adding proper auth and access control after the fact is one of the most common and painful mistakes in web development. Getting it right from the start means every feature you add afterward is automatically protected.

Why auth first?

Every feature in your app will depend on knowing who the current user is:

  • Data belongs to a specific user
  • Private content must stay private
  • Database tables must enforce that users can only see their own rows

If you build features first and auth later, you end up retrofitting security into code that wasn't designed for it. Instead, we'll establish the auth foundation now, then build everything else on top of it.

What we're building

In this step, Claude Code will scaffold the entire React + Vite project and create two things:

  1. A landing page — the public page users see before they log in, with a "Sign in with GitHub" button
  2. An authenticated dashboard — a protected page users land on after logging in, which confirms auth is working

We'll also verify that:

  • Unauthenticated users can't access protected pages
  • Supabase sessions work correctly
  • Row Level Security (RLS) is enabled on the database from day one

Step 1: Scaffold the React + Vite project

Type this inside Claude Code:

Claude Code prompt:

Scaffold a React + Vite project in this directory. Use Tailwind CSS for styling and React Router for client-side routing. Install the Supabase client library (@supabase/supabase-js). We are connecting to a hosted Supabase project using the environment variables in .env.

Claude will:

  • Initialize the Vite project with npm create vite@latest
  • Install React Router and the Supabase package
  • Set up Tailwind CSS
  • Set up the basic project structure

This will take a minute. When it's done, Claude will tell you the project is ready.

Step 2: Set up the Supabase client

Still inside Claude Code, tell it to wire up the Supabase connection:

Claude Code prompt:

Set up the Supabase client for this React + Vite project. Create a single browser-side client using the environment variables VITE_SUPABASE_URL and VITE_SUPABASE_PUBLISHABLE_KEY. Export it from a src/lib/supabase.js file so it can be imported anywhere in the app.

Claude will create a small utility file that initializes the Supabase client. Because this is a pure client-side React app (no server), one client is all we need — it runs in the browser and manages the user's auth session automatically.

Step 3: Build the landing page

Claude Code prompt:

Create a landing page at the root route (/) that:
- Is publicly accessible (no login required)
- Shows the app name and a short description
- Has a "Sign in with GitHub" button that triggers Supabase GitHub OAuth
- Redirects authenticated users to /dashboard automatically
Keep the design clean and simple using Tailwind.

Claude will build the landing page component and the OAuth callback route that Supabase needs to complete the login flow.

Step 4: Build the protected dashboard

Claude Code prompt:

Create a dashboard page at /dashboard that:
- Requires the user to be logged in
- Redirects unauthenticated users to / (the landing page)
- Displays a welcome message with the user's name or email from their session
- Has a "Sign out" button that clears the session and redirects to /

This gives us a protected page we can actually test — and it becomes the home base for all the features we'll build next.

Before we create our first database table, we need to install the Supabase CLI — a command-line tool that lets Claude Code run SQL directly against your database. Without it, you'd have to manually copy and paste SQL into the Supabase dashboard every time you need a new table or change.

For this step, keep Claude Code running and open a second terminal window:

  • Mac: Press Cmd + T in Terminal to open a new tab, or open a new Terminal window
  • Windows: Open a new Git Bash window
  • Linux: Open a new terminal tab with Ctrl + Shift + T

In the new terminal, navigate to your project folder:

cd ~/repos/your-repo-name

Now that your project has a package.json from the Vite scaffold, install the CLI as a dev dependency:

npm install supabase --save-dev

Next, log in to your Supabase account through the CLI:

npx supabase login

This will open a browser window — authorize the CLI and return to your terminal.

Finally, link the CLI to your Supabase project:

npx supabase link

Select your project from the list and enter your database password when prompted (the one you saved during Supabase account setup).

Don't have your database password?

If you didn't save it during account setup, go to your Supabase dashboard > Project Settings > General > Reset database password. Generate a new one and save it this time.

Verify it worked:

npx supabase projects list

You should see your project listed with an active status. The CLI is now connected.

Now switch back to your Claude Code terminal — it's still running with all your context intact.

Step 6: Create your first database table with Row Level Security

This is where we create the first real table in your database. What this table looks like depends on your app idea — describe your data to Claude and it will create the right schema.

Because you just set up the Supabase CLI, Claude Code can run this SQL directly — no dashboard copy-pasting needed.

Type something like this inside Claude Code (adapt it to your app):

Claude Code prompt:

Create a SQL migration file called supabase/migrations/001_create_[your_table].sql that creates a "[your_table]" table with the following columns:

- id (uuid, primary key, default gen_random_uuid())
- user_id (uuid, not null, references auth.users)
- [your columns here — describe what data your app needs to store]
- created_at (timestamptz, default now())
- updated_at (timestamptz, default now())

Enable Row Level Security on the table. Add policies so users can only select, insert, update, and delete their own rows (where user_id matches auth.uid()).

Then run the migration against our linked Supabase project using the Supabase CLI.

Claude will write the SQL file and then run it with:

npx supabase db execute --file supabase/migrations/001_create_[your_table].sql

Your database is updated automatically. You'll see the new table appear in your Supabase dashboard under Table Editor if you want to confirm it worked.

What is RLS?

Row Level Security is a Supabase (and PostgreSQL) feature that enforces access rules at the database level. Even if your application code has a bug and accidentally tries to fetch another user's data, the database would still refuse to return it. It's a critical safety net that Claude will apply to every table it creates.

Migrations as files

Keeping SQL in migration files (rather than running it ad-hoc) means your database schema lives alongside your code in Git. You'll always know exactly what changes were made and when.

Step 7: Test the full auth flow

Let's make sure everything works before moving forward. You need to start a development server — a small program that runs your app on your own computer so you can preview it in a browser. It watches your files for changes and automatically refreshes the page whenever something updates.

Keep Claude Code running. In your second terminal (the one you used for the Supabase CLI), run:

npm run dev

You should see output like:

  VITE v6.x.x  ready in 300ms

➜ Local: http://localhost:5173/
What is localhost:5173?

localhost means "this computer" — the app is only visible to you, not on the internet yet. 5173 is the port number (like a channel) that Vite uses. This is completely normal for development. When you're ready to share your app with the world, we'll deploy it to Cloudflare later.

Now open http://localhost:5173 in your browser and walk through this:

  1. You should see the landing page with the "Sign in with GitHub" button
  2. Click it — you'll be redirected to GitHub to authorize your app
  3. After authorizing, you'll be redirected back and land on /dashboard
  4. You should see your name or email from your GitHub account
  5. Click "Sign out" — you should be sent back to the landing page
  6. Try navigating directly to http://localhost:5173/dashboard — you should be redirected to / because you're not logged in

If all of that works, your auth foundation is solid.

If something doesn't work

Switch back to your Claude Code terminal and paste the error message directly in. Because you kept Claude Code running, it still has the full context of everything you've built. Claude can read your file structure and error logs to diagnose what went wrong. Common issues at this stage are misconfigured OAuth callback URLs or environment variable typos.

What just happened?

You built the entire authentication layer for your app:

  • A public landing page with GitHub login
  • An OAuth flow handled by Supabase (you didn't write any of the auth logic)
  • A protected route that redirects unauthenticated users
  • A database table with RLS — your data model is protected at the database level from the start

Every feature you build from here will inherit this foundation. Your data will be private, your pages will be protected, and your app will be secure by default.

What's next?

Authentication is working and your foundation is solid. Now it's time to start vibing — building out the actual features of your app with Claude Code.