How to Deploy a Full-Stack App in 10 Minutes with Vercel and Supabase

Tutorial · 9 min read 🔄 Affiliate Links

🔍 Want the best deal? Check current prices and availability.

Compare Prices →
Disclosure: When you buy through links on our site, we may earn a commission. This doesn't affect our editorial independence.

You’ve got an idea. You’ve written some code. Now you need to get it live – fast. The old way of spinning up a VPS, configuring nginx, setting up PostgreSQL, and managing secrets takes hours. But with Vercel for the frontend and Supabase for the backend, you can go from git push to a live, production-ready full-stack app in under ten minutes.

I’ve used this exact stack for half a dozen side projects and client apps. It’s not perfect for every use case (I’ll get to that), but for 90% of indie hackers and small teams, it’s the fastest path to shipping. Let’s walk through it.


Prerequisites

Before we start, make sure you have:

  • Node.js 18+ and npm installed
  • A GitHub account (free)
  • A Vercel account – sign up at vercel.com (free tier is generous)
  • A Supabase account – sign up at supabase.com (free tier includes 500 MB database, 50k users, and real-time)
  • Basic familiarity with React or Next.js (we’ll use Next.js, but the same steps apply to any framework Vercel supports)

If you don’t have a project ready, I’ll show you how to scaffold one in step 1.


Step 1: Create a Next.js App

Vercel is built by the creators of Next.js, so Next.js is the most natural choice. But you can also deploy React, Svelte, Vue, or static sites. For this tutorial, we’ll use Next.js with the App Router.

Open your terminal and run:

npx create-next-app@latest my-fullstack-app

Select these options when prompted:

  • TypeScript? Yes (you can use JavaScript, but TypeScript works better with Supabase types)
  • ESLint? Yes
  • Tailwind CSS? Yes (handy for quick styling)
  • src/ directory? Yes
  • App Router? Yes (this is the modern approach)
  • Import alias? Default is fine

Once it finishes, go into the project folder and start the dev server:

cd my-fullstack-app

npm run dev

Open http://localhost:3000 and you should see the default Next.js page. Good. Now let’s add the backend.


Step 2: Set Up Supabase

Supabase gives you a PostgreSQL database, authentication, file storage, and real-time subscriptions – all in one dashboard. It’s like Firebase but with SQL and open-source.

  • Log into your Supabase account and click New project.
  • Give it a name (e.g., my-fullstack-app), set a secure database password, and choose a region close to your users (I usually pick us-east-1 or eu-west-1).
  • Wait about 30 seconds for the database to provision.

Once the project is ready, go to the SQL Editor and run this query to create a simple todos table:

create table todos (

id uuid default gen_random_uuid() primary key,

user_id uuid references auth.users not null,

task text not null,

is_complete boolean default false,

inserted_at timestamp with time zone default timezone('utc'::text, now()) not null

);

-- Enable Row Level Security (RLS)

alter table todos enable row level security;

-- Create a policy so users can only see their own todos

create policy "Users can view their own todos"

on todos for select

using ( auth.uid() = user_id );

-- Users can insert their own todos

create policy "Users can insert their own todos"

on todos for insert

with check ( auth.uid() = user_id );

This sets up a basic table with Row Level Security – a must for any app that stores user data. Supabase’s RLS is incredibly powerful and saves you from writing backend code for authorization.

Now go to Project Settings > API and copy your anon public key and project URL. We’ll need those in the next step.


Step 3: Connect the Frontend to Supabase

Back in your Next.js project, install the Supabase client library:

npm install @supabase/supabase-js @supabase/ssr

The @supabase/ssr package is the recommended way to use Supabase with Next.js App Router – it handles cookies and server-side rendering properly.

Create a file src/lib/supabase.ts:

import { createBrowserClient } from '@supabase/ssr'

export const createClient = () =>

createBrowserClient(

process.env.NEXT_PUBLIC_SUPABASE_URL!,

process.env.NEXT_PUBLIC_SUPABASE_ANON_KEY!

)

Now create a .env.local file in the root of your project and add the two values from Supabase:

NEXT_PUBLIC_SUPABASE_URL=https://your-project-id.supabase.co

NEXT_PUBLIC_SUPABASE_ANON_KEY=your-anon-key

Important: Vercel will need these same environment variables later. We’ll set them in the deployment step.

Now let’s test the connection. Edit src/app/page.tsx to fetch todos from Supabase:

import { createClient } from '@/lib/supabase'

export default async function Home() {

const supabase = createClient()

const { data: todos } = await supabase.from('todos').select('*')

return (

My Todos

{JSON.stringify(todos, null, 2)}

)

}

Restart the dev server and visit http://localhost:3000. You should see an empty array [] because there’s no data yet. That’s fine – it means the connection works.


Step 4: Deploy to Vercel

This is where the magic happens. Vercel detects your framework automatically and handles the build, SSL, and CDN for you.

First, push your project to GitHub:

git init

git add .

git commit -m "Initial commit"

gh repo create my-fullstack-app --public --push

(If you don’t have the GitHub CLI, create a repo manually on GitHub and push.)

Now go to your Vercel dashboard and click Add New > Project. Connect your GitHub account and select the my-fullstack-app repo.

Vercel will auto-detect Next.js. Before you click Deploy, expand the Environment Variables section and add the same two variables you used in .env.local:

  • NEXT_PUBLIC_SUPABASE_URL
  • NEXT_PUBLIC_SUPABASE_ANON_KEY

Click Deploy. Within 30 seconds, you’ll get a live URL like https://my-fullstack-app.vercel.app.

Your app is now live, but it’s not useful yet – there’s no way for users to log in and create todos. Let’s fix that.


Step 5: Add Authentication

Supabase Auth supports email/password, magic links, Google, GitHub, and more. We’ll add a simple magic link flow so users can sign in with just their email.

First, in the Supabase dashboard, go to Authentication > Providers and enable Email (if not already). Under Confirm email, you can choose to require confirmation or not – for testing, I turn it off.

Now create a new file src/app/auth/callback/route.ts (this handles the magic link redirect):

import { createRouteHandlerClient } from '@supabase/auth-helpers-nextjs'

import { cookies } from 'next/headers'

import { NextRequest, NextResponse } from 'next/server'

export async function GET(request: NextRequest) {

const requestUrl = new URL(request.url)

const code = requestUrl.searchParams.get('code')

if (code) {

const supabase = createRouteHandlerClient({ cookies })

await supabase.auth.exchangeCodeForSession(code)

}

return NextResponse.redirect(requestUrl.origin)

}

Next, create a simple login component at src/app/login/page.tsx:

'use client'

import { createClient } from '@/lib/supabase'

import { useState } from 'react'

import { useRouter } from 'next/navigation'

export default function LoginPage() {

const [email, setEmail] = useState('')

const [loading, setLoading] = useState(false)

const [message, setMessage] = useState('')

const supabase = createClient()

const router = useRouter()

const handleLogin = async (e: React.FormEvent) => {

e.preventDefault()

setLoading(true)

const { error } = await supabase.auth.signInWithOtp({

email,

options: { shouldCreateUser: true },

})

if (error) {

setMessage(error.message)

} else {

setMessage('Check your email for the magic link!')

}

setLoading(false)

}

return (

Sign in with Magic Link

type="email"

placeholder="[email protected]"

value={email}

onChange={(e) => setEmail(e.target.value)}

className="w-full border p-2 rounded"

required

/>

type="submit"

disabled={loading}

className="bg-blue-600 text-white px-4 py-2 rounded hover:bg-blue-700"

>

{loading ? 'Sending...' : 'Send Magic Link'}

{message &&

{message}

}

)

}

Finally, update the main page to show a login button or a list of todos based on authentication status. I’ll keep it simple – create a src/app/layout.tsx that checks the session and redirects unauthenticated users to /login.

Now commit and push again. Vercel will automatically redeploy. Test the flow: go to your live URL, enter an email, and you’ll receive a magic link. Click it, and you’re logged in.

You now have a full-stack app with authentication, a database, and real-time capabilities – all deployed in minutes.


Troubleshooting

Even with a smooth setup, you might hit a few snags. Here are the common ones and how to fix them.

“401 Unauthorized” when fetching data

This usually means Row Level Security is blocking the query because the user isn’t authenticated. Make sure you’re passing the session properly. In the App Router, use createServerComponentClient from @supabase/auth-helpers-nextjs for server components, and createClientComponentClient for client components.

Check that the redirect URL in Supabase Auth settings includes https://your-app.vercel.app/auth/callback. Also verify that the NEXT_PUBLIC_SITE_URL environment variable in Vercel is set to your production URL.

CORS errors

Supabase’s API allows requests from any origin by default, but if you’re using a custom domain, make sure the domain is added in Supabase’s Authentication settings under Site URL.

Build fails on Vercel

Most often it’s a missing environment variable. Double-check that both NEXT_PUBLIC_SUPABASE_URL and NEXT_PUBLIC_SUPABASE_ANON_KEY are set in the Vercel project settings. Also ensure your TypeScript compiles without errors locally.


Conclusion

Vercel + Supabase is the fastest way I’ve found to ship a full-stack app that scales from zero to thousands of users. You get a global CDN, serverless functions, a managed PostgreSQL database, and built-in auth – all on generous free tiers.

Pros:

  • Setup time: under 10 minutes for a basic app
  • No DevOps: Vercel handles SSL, CDN, and deployments
  • Supabase’s RLS eliminates the need for a custom backend
  • Great developer experience with TypeScript and real-time subscriptions

Cons:

  • Vendor lock-in: moving off Vercel or Supabase later is nontrivial
  • Supabase free tier limits: 500 MB database, 50k monthly active users
  • Vercel’s serverless functions have cold starts (though Edge Functions help)
  • Not ideal for apps requiring complex background jobs or custom server logic

Verdict: If you’re an indie hacker or a small team building a SaaS, internal tool, or MVP, this stack is hard to beat. For anything more complex (e.g., heavy file processing, WebSockets beyond real-time, or multi-region databases), you might want to consider a VPS with Docker or a platform like Railway that gives you more control.

But for 90% of full-stack apps, Vercel + Supabase is the right choice. Go ship something.


P.S. – If you want to host your own database for more control, check out DigitalOcean Managed Databases. They start at $15/month and give you dedicated PostgreSQL with automatic backups.

🔍 Want the best deal? Check current prices and availability.

Compare Prices →
D

Dev Tool Rank Editorial Team

We're a team of tech enthusiasts who test and review tools so you don't have to. Our reviews are independent — we only recommend what we'd actually use ourselves.