Skip to main content
RapidDev - Software Development Agency

How to Build Auction platform with V0

Build a real-time auction platform with V0 using Next.js, Supabase Realtime, and Stripe. You'll get live bidding with instant updates, anti-sniping countdown extensions, secure payment processing for winners, and admin auction management — all in about 2-4 hours without local setup.

What you'll build

  • Live auction page with real-time bid feed using Supabase Realtime subscriptions updating within 200ms
  • Anti-sniping countdown extension that adds 2 minutes when bids arrive in the final 5 minutes
  • Stripe payment integration for winners with checkout session creation and webhook verification
  • Auction listing grid with countdown timers, current price, and bid count using shadcn/ui Cards
  • Bid race condition prevention using Supabase RPC with SELECT FOR UPDATE transaction locking
  • Auction creation form for sellers with image upload, reserve price, and scheduling
Book a free consultation
4.9Clutch rating
600+Happy partners
17+Countries served
190+Team members
Advanced12 min read2-4 hoursV0 Premium or higherApril 2026RapidDev Engineering Team
TL;DR

Build a real-time auction platform with V0 using Next.js, Supabase Realtime, and Stripe. You'll get live bidding with instant updates, anti-sniping countdown extensions, secure payment processing for winners, and admin auction management — all in about 2-4 hours without local setup.

What you're building

Online auctions require real-time updates, race condition prevention, and reliable payment processing — three of the hardest problems in web development. Users need to see competing bids instantly, the system must prevent two people from bidding on stale prices, and winners need to pay seamlessly.

V0 handles the UI complexity by generating the auction listing grid, real-time bid feed, and checkout flow from prompts. Supabase via the Connect panel provides the database with Realtime channels for instant bid updates. Stripe via Vercel Marketplace handles winner payments.

The architecture uses Next.js Server Components for the auction listing and detail pages, a Client Component for the real-time bid feed subscribed to Supabase Realtime, an API route for bid validation with database-level transaction locking, a Stripe webhook handler for payment confirmation, and Server Actions for auction creation and management.

Final result

A fully functional auction platform with real-time bidding, anti-sniping protection, Stripe payment for winners, seller auction management, and a responsive auction gallery with countdown timers.

Tech stack

V0AI Code Generator
Next.jsFull-Stack Framework
Tailwind CSSStyling
shadcn/uiComponent Library
SupabaseDatabase
Supabase RealtimeRealtime
StripePayments

Prerequisites

  • A V0 account (Premium plan recommended for complex multi-page builds)
  • A Supabase project with Realtime enabled (free tier works — connect via V0's Connect panel)
  • A Stripe account (test mode works for development)
  • Understanding of auction mechanics (reserve prices, bid increments, time extensions)

Build steps

1

Set up the database schema with auction tables and bid constraints

Create a new V0 project, connect Supabase and Stripe via the Connect panel. Then prompt V0 to create the auction, bid, and payment tables with proper constraints. The schema includes a database trigger for anti-sniping that extends the auction end time when late bids arrive.

prompt.txt
1// Paste this prompt into V0's AI chat:
2// Build a real-time auction platform with Supabase. Create these tables:
3// 1. auctions: id (uuid PK), seller_id (uuid FK to auth.users), title (text), description (text), images (text[]), starting_price (numeric), reserve_price (numeric), current_price (numeric), status (text CHECK in 'draft','active','ended','sold'), starts_at (timestamptz), ends_at (timestamptz), winner_id (uuid FK)
4// 2. bids: id (uuid PK), auction_id (uuid FK), bidder_id (uuid FK to auth.users), amount (numeric), created_at (timestamptz), CONSTRAINT bid_positive CHECK (amount > 0)
5// 3. payments: id (uuid PK), auction_id (uuid FK), buyer_id (uuid FK), stripe_payment_intent_id (text), amount (numeric), status (text), created_at (timestamptz)
6// Add a database trigger: on bids INSERT, update auctions.current_price to the bid amount, and if the bid is within 5 minutes of ends_at, extend ends_at by 2 minutes (anti-sniping).
7// Enable Realtime on the bids table.
8// Add RLS policies.

Pro tip: Enable Realtime on the bids table in the Supabase Dashboard under Database > Replication. This is required for the real-time bid feed to work on the auction detail page.

Expected result: Supabase is connected with tables created, the anti-sniping trigger is active, and Realtime is enabled on the bids table.

2

Build the bid submission API route with race condition prevention

Create the API route that validates and inserts bids. The key challenge is preventing two users from bidding on the same stale price simultaneously. Use a Supabase RPC function with SELECT FOR UPDATE to lock the auction row during bid validation.

app/api/bids/route.ts
1import { NextRequest, NextResponse } from 'next/server'
2import { createClient } from '@supabase/supabase-js'
3
4const supabase = createClient(
5 process.env.SUPABASE_URL!,
6 process.env.SUPABASE_SERVICE_ROLE_KEY!
7)
8
9export async function POST(req: NextRequest) {
10 const { auctionId, bidderId, amount } = await req.json()
11
12 const { data, error } = await supabase.rpc('place_bid', {
13 p_auction_id: auctionId,
14 p_bidder_id: bidderId,
15 p_amount: amount,
16 p_min_increment: 1.0,
17 })
18
19 if (error) {
20 const message = error.message.includes('too low')
21 ? 'Your bid is below the current price plus minimum increment'
22 : error.message.includes('not active')
23 ? 'This auction is not currently active'
24 : error.message
25 return NextResponse.json({ error: message }, { status: 400 })
26 }
27
28 return NextResponse.json({ bid: data })
29}
30
31// The RPC function in Supabase SQL editor:
32// CREATE OR REPLACE FUNCTION place_bid(
33// p_auction_id uuid, p_bidder_id uuid, p_amount numeric, p_min_increment numeric
34// ) RETURNS uuid AS $$
35// DECLARE
36// v_auction record;
37// v_bid_id uuid;
38// BEGIN
39// SELECT * INTO v_auction FROM auctions WHERE id = p_auction_id FOR UPDATE;
40// IF v_auction.status != 'active' THEN RAISE EXCEPTION 'Auction not active';
41// END IF;
42// IF p_amount < v_auction.current_price + p_min_increment THEN
43// RAISE EXCEPTION 'Bid too low';
44// END IF;
45// INSERT INTO bids (auction_id, bidder_id, amount)
46// VALUES (p_auction_id, p_bidder_id, p_amount) RETURNING id INTO v_bid_id;
47// UPDATE auctions SET current_price = p_amount WHERE id = p_auction_id;
48// RETURN v_bid_id;
49// END; $$ LANGUAGE plpgsql;

Expected result: The bid API route calls a Supabase RPC function that locks the auction row, validates the bid amount is above the current price plus minimum increment, and inserts the bid atomically.

3

Build the real-time auction detail page with live bid feed

Create the auction detail page that shows the current price, countdown timer, bid history, and a bid input. Subscribe to Supabase Realtime on the bids table so all viewers see new bids within 200ms without polling.

prompt.txt
1// Paste this prompt into V0's AI chat:
2// Build a real-time auction detail page at app/auctions/[id]/page.tsx.
3// Requirements:
4// - Server Component fetches initial auction data and last 20 bids
5// - Client Component wraps the bid feed with Supabase Realtime subscription
6// - Subscribe to INSERT events on the bids table filtered by auction_id
7// - Show current price in large font with animated update on new bids
8// - Countdown timer that updates every second showing time remaining
9// - Timer extends in real-time when anti-sniping trigger fires (subscribe to auction row changes)
10// - Bid history in a ScrollArea showing bidder avatar, amount, and timestamp
11// - Bid input with shadcn/ui Input for amount and Button to submit
12// - AlertDialog for bid confirmation showing the amount before submitting
13// - Skeleton loading states while initial data loads
14// - Badge for auction status (active, ended, sold)
15// - Image carousel for auction images using shadcn/ui Carousel

Pro tip: Subscribe to both the bids table (for new bid inserts) and the auctions table (for end time extensions from the anti-sniping trigger) on the same channel. This keeps the countdown timer and bid feed in sync.

Expected result: The auction page shows live bid updates, a countdown timer that extends on late bids, an image carousel, and a bid submission form with confirmation dialog.

4

Create the Stripe payment flow for auction winners

When an auction ends, the winner needs to pay. Build the payment flow using Stripe Checkout — create a session with the winning bid amount and handle the webhook to confirm payment and update the auction status to 'sold'.

app/api/webhooks/stripe/route.ts
1import { NextRequest, NextResponse } from 'next/server'
2import Stripe from 'stripe'
3import { createClient } from '@supabase/supabase-js'
4
5const stripe = new Stripe(process.env.STRIPE_SECRET_KEY!)
6const supabase = createClient(
7 process.env.SUPABASE_URL!,
8 process.env.SUPABASE_SERVICE_ROLE_KEY!
9)
10
11export async function POST(req: NextRequest) {
12 const body = await req.text()
13 const sig = req.headers.get('stripe-signature')!
14
15 let event: Stripe.Event
16 try {
17 event = stripe.webhooks.constructEvent(
18 body,
19 sig,
20 process.env.STRIPE_WEBHOOK_SECRET!
21 )
22 } catch {
23 return NextResponse.json({ error: 'Invalid signature' }, { status: 400 })
24 }
25
26 if (event.type === 'checkout.session.completed') {
27 const session = event.data.object as Stripe.Checkout.Session
28 const auctionId = session.metadata?.auction_id
29
30 if (auctionId) {
31 await supabase.from('payments').insert({
32 auction_id: auctionId,
33 buyer_id: session.metadata?.buyer_id,
34 stripe_payment_intent_id: session.payment_intent as string,
35 amount: (session.amount_total ?? 0) / 100,
36 status: 'paid',
37 })
38
39 await supabase
40 .from('auctions')
41 .update({ status: 'sold' })
42 .eq('id', auctionId)
43 }
44 }
45
46 return NextResponse.json({ received: true })
47}

Pro tip: Always use request.text() — not request.json() — for the Stripe webhook body. Stripe signature verification requires the raw body, and parsing it first corrupts the signature check.

Expected result: When an auction winner completes Stripe Checkout, the webhook creates a payment record and updates the auction status to sold.

5

Build the auction listing grid and seller creation form

Create the public auction listing page and the seller's auction creation form. The listing page shows active auctions in a responsive grid with countdown timers, current prices, and bid counts. Sellers can create new auctions with images, descriptions, reserve prices, and scheduling.

prompt.txt
1// Paste this prompt into V0's AI chat:
2// Build two pages:
3// 1. Auction listing at app/auctions/page.tsx (Server Component):
4// - Grid of auction Cards with image, title, current price, time remaining countdown, bid count
5// - Badge for auction status (active, ending soon for < 1 hour)
6// - Select for category filter and sort (ending soonest, highest price, newest)
7// - Skeleton loading states
8// - Link to detail page
9// 2. Create auction at app/auctions/new/page.tsx ('use client'):
10// - Form with Input for title, Textarea for description
11// - Image upload (multiple) to Supabase Storage
12// - Input for starting_price and reserve_price
13// - DatePicker for starts_at and ends_at
14// - Button to submit via Server Action
15// - Form validation with zod

Expected result: The listing page shows a responsive grid of active auctions with countdown timers. Sellers can create new auctions with images, pricing, and scheduled start/end times.

Complete code

app/api/bids/route.ts
1import { NextRequest, NextResponse } from 'next/server'
2import { createClient } from '@supabase/supabase-js'
3
4const supabase = createClient(
5 process.env.SUPABASE_URL!,
6 process.env.SUPABASE_SERVICE_ROLE_KEY!
7)
8
9export async function POST(req: NextRequest) {
10 const { auctionId, bidderId, amount } = await req.json()
11
12 if (!auctionId || !bidderId || !amount) {
13 return NextResponse.json(
14 { error: 'Missing required fields' },
15 { status: 400 }
16 )
17 }
18
19 const { data, error } = await supabase.rpc('place_bid', {
20 p_auction_id: auctionId,
21 p_bidder_id: bidderId,
22 p_amount: amount,
23 p_min_increment: 1.0,
24 })
25
26 if (error) {
27 const status = error.message.includes('not active') ? 409 : 400
28 return NextResponse.json({ error: error.message }, { status })
29 }
30
31 return NextResponse.json({ bid_id: data })
32}
33
34export async function GET(req: NextRequest) {
35 const { searchParams } = new URL(req.url)
36 const auctionId = searchParams.get('auction_id')
37
38 if (!auctionId) {
39 return NextResponse.json(
40 { error: 'auction_id is required' },
41 { status: 400 }
42 )
43 }
44
45 const { data, error } = await supabase
46 .from('bids')
47 .select('id, amount, created_at, bidder_id')
48 .eq('auction_id', auctionId)
49 .order('created_at', { ascending: false })
50 .limit(50)
51
52 if (error) {
53 return NextResponse.json({ error: error.message }, { status: 500 })
54 }
55
56 return NextResponse.json({ bids: data })
57}

Customization ideas

Add proxy bidding

Implement automatic bidding where users set a maximum bid and the system automatically outbids competitors by the minimum increment up to their max, using a Supabase trigger on bid insert.

Add auction categories and search

Add a categories table and full-text search on auction titles and descriptions using Supabase's built-in tsvector and GIN indexes.

Add email notifications for outbid events

When a bidder is outbid, send them an email notification using Resend called from the bid trigger or an API route so they can re-bid before the auction ends.

Add a watch list

Let users save auctions to a watch list with a toggle button and show upcoming ending times for watched auctions on their dashboard.

Add seller ratings

After an auction is completed and paid, allow buyers to rate sellers with a 1-5 star rating and text review, displayed on the seller's profile and future auctions.

Common pitfalls

Pitfall: Validating bid amounts in JavaScript instead of the database

How to avoid: Use a Supabase RPC function with SELECT FOR UPDATE that locks the auction row, validates the bid is above current_price + min_increment, and inserts atomically — all within a single transaction.

Pitfall: Using request.json() for Stripe webhook body parsing

How to avoid: Always use request.text() to get the raw body, then pass it to stripe.webhooks.constructEvent() along with the stripe-signature header and your webhook secret.

Pitfall: Not enabling Supabase Realtime on the bids table

How to avoid: Go to Supabase Dashboard, navigate to Database, then Replication, and enable Realtime for the bids table. Also enable it for the auctions table to sync countdown timer extensions.

Pitfall: Not handling the anti-sniping timer extension on the frontend

How to avoid: Subscribe to changes on the auctions table via Supabase Realtime. When an UPDATE event arrives with a new ends_at value, update the countdown timer's target time.

Best practices

  • Use Supabase RPC functions with SELECT FOR UPDATE for bid validation to prevent race conditions at the database level
  • Enable Realtime on both the bids and auctions tables — bids for the live feed, auctions for anti-sniping timer extensions
  • Always use request.text() for Stripe webhook body parsing to preserve the raw bytes for signature verification
  • Store STRIPE_SECRET_KEY and STRIPE_WEBHOOK_SECRET in V0's Vars tab without NEXT_PUBLIC_ prefix
  • Implement anti-sniping as a database trigger so it fires automatically regardless of how bids are inserted
  • Use Server Components for the auction listing page and Client Components only for the real-time bid feed and countdown timer
  • Use Design Mode (Option+D) to visually adjust auction card layouts, countdown timer styling, and bid feed density without spending credits
  • Add Skeleton loading states to all auction pages since real-time data may take a moment to hydrate

AI prompts to try

Copy these prompts to build this project faster.

ChatGPT Prompt

I'm building a real-time auction platform with Next.js App Router, Supabase, and Stripe. I need to handle bid race conditions, real-time bid updates, anti-sniping countdown extensions, and payment processing for winners. Help me design a Supabase RPC function that uses SELECT FOR UPDATE to atomically validate and insert bids.

Build Prompt

Build a real-time bid feed component that subscribes to Supabase Realtime INSERT events on the bids table filtered by auction_id. The component should: display new bids instantly in a ScrollArea with auto-scroll to bottom, animate the current price update with a brief highlight, update the bid count, and merge incoming bids with the initial server-fetched list without duplicates by checking bid id.

Frequently asked questions

How does the anti-sniping protection work?

A database trigger fires on every bid insert. If the bid arrives within 5 minutes of the auction's end time, the trigger automatically extends ends_at by 2 minutes. This prevents last-second sniping and gives all bidders a fair chance to respond. The frontend detects the time extension via Supabase Realtime.

How do I prevent two people from placing the same bid simultaneously?

Use a Supabase RPC function with SELECT FOR UPDATE that locks the auction row within a transaction. It checks that the new bid exceeds current_price plus the minimum increment, inserts the bid, and updates current_price atomically. If two bids arrive simultaneously, one waits for the lock and then re-checks the price.

What V0 plan do I need for an auction platform?

V0 Premium is recommended because the auction platform requires multiple complex pages (listing, detail with real-time, creation form), API routes (bids, payments, webhooks), and Supabase Realtime integration. The free plan's credits will not be sufficient.

Can the free Supabase tier handle real-time auctions?

Yes for development and small-scale use. The free tier supports 200 concurrent Realtime connections, which is enough for testing and early launches. For production auctions with many simultaneous viewers, upgrade to Supabase Pro.

How do I deploy the auction platform?

Click Share then Publish to Production in V0 for instant Vercel deployment. After publishing, register the Stripe webhook URL at https://yourdomain.vercel.app/api/webhooks/stripe in the Stripe Dashboard. Select the checkout.session.completed event.

Can RapidDev help build a custom auction platform?

Yes. RapidDev has built 600+ apps including complex real-time platforms with bidding systems, escrow payments, and fraud detection. Book a free consultation to discuss your specific auction platform requirements.

RapidDev

Talk to an Expert

Our team has built 600+ apps. Get personalized help with your project.

Book a free consultation

Need help building your app?

Our experts have built 600+ apps and can accelerate your development. Book a free consultation — no strings attached.

Book a free consultation

We put the rapid in RapidDev

Need a dedicated strategic tech and growth partner? Discover what RapidDev can do for your business! Book a call with our team to schedule a free, no-obligation consultation. We'll discuss your project and provide a custom quote at no cost.