Skip to main content
RapidDev - Software Development Agency

How to Build Job board with V0

Build a niche job board with V0 using Next.js, Supabase, Stripe, and shadcn/ui. Employers post and pay for featured listings via Stripe Checkout, applicants search and apply with resume uploads, and webhook-verified payments automatically promote listings — all in about 1-2 hours.

What you'll build

  • Searchable job listing page with filters for location, job type, and salary range using shadcn/ui Sheet sidebar
  • Employer dashboard for managing job postings, viewing applicants, and tracking application status
  • Stripe Checkout integration for paid featured listings with webhook-verified payment processing
  • Quick-apply Dialog with resume upload to Supabase Storage and cover letter Textarea
  • Job detail page with company info, salary range, and one-click save-to-favorites functionality
  • Applicant tracking system with status Badge indicators (new, reviewed, shortlisted, rejected)
Book a free consultation
4.9Clutch rating
600+Happy partners
17+Countries served
190+Team members
Intermediate11 min read1-2 hoursV0 Premium or higherApril 2026RapidDev Engineering Team
TL;DR

Build a niche job board with V0 using Next.js, Supabase, Stripe, and shadcn/ui. Employers post and pay for featured listings via Stripe Checkout, applicants search and apply with resume uploads, and webhook-verified payments automatically promote listings — all in about 1-2 hours.

What you're building

Vertical job boards (remote-only, AI jobs, climate tech) outperform generic platforms because they attract focused candidates and employers willing to pay for visibility. A niche job board with paid featured listings is a proven revenue model that can be built in hours instead of months.

V0 makes this fast by generating the job listing UI, employer dashboard, and Stripe integration from prompts. Connect Supabase via the Connect panel for the database and auth, add Stripe via the Vercel Marketplace for instant payment setup, and you have a working job board on Vercel.

The architecture uses Next.js App Router with Server Components for SEO-friendly job pages, Stripe Checkout for payment processing, Supabase Storage for resume uploads, and webhook handlers for automated featured listing activation.

Final result

A fully functional job board with employer posting flows, applicant tracking, Stripe-powered featured listings, searchable job directory, and resume upload support.

Tech stack

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

Prerequisites

  • A V0 account (Premium or higher recommended)
  • A Supabase project (free tier works — connect via V0's Connect panel)
  • A Stripe account (test mode works — connect via Vercel Marketplace)
  • Your niche focus (e.g., remote jobs, AI roles, climate tech positions)

Build steps

1

Set up the project with Supabase and Stripe

Create a new V0 project. Use the Connect panel to add Supabase for the database and auth. Then add Stripe via the Vercel Marketplace — this auto-provisions your test API keys into the Vars tab.

prompt.txt
1// Paste this prompt into V0's AI chat:
2// Build a job board platform. Create a Supabase schema with these tables:
3// 1. companies: id (uuid PK), name (text), logo_url (text), website (text), owner_id (uuid FK to auth.users)
4// 2. jobs: id (uuid PK), company_id (uuid FK to companies), title (text), description (text), location (text), salary_min (int), salary_max (int), job_type (text), is_featured (boolean DEFAULT false), status (text DEFAULT 'draft'), published_at (timestamptz), expires_at (timestamptz), stripe_payment_id (text)
5// 3. applications: id (uuid PK), job_id (uuid FK to jobs), applicant_name (text), email (text), resume_url (text), cover_letter (text), status (text DEFAULT 'new'), created_at (timestamptz)
6// 4. saved_jobs: user_id (uuid FK to auth.users), job_id (uuid FK to jobs), PRIMARY KEY (user_id, job_id)
7// Add RLS policies: anyone can read published jobs, only company owners can manage their jobs and view applications.

Pro tip: Add Stripe via the Vercel Marketplace integration in V0 — it auto-provisions STRIPE_SECRET_KEY and NEXT_PUBLIC_STRIPE_PUBLISHABLE_KEY into your Vars tab, saving manual configuration.

Expected result: Supabase is connected with all tables created, Stripe keys are in the Vars tab, and RLS policies are active.

2

Build the searchable job listing page

Create the public-facing job board page with search, filters, and featured job highlighting. Use Server Components for SEO and a client component for the interactive filter sidebar.

prompt.txt
1// Paste this prompt into V0's AI chat:
2// Create a job listing page at app/jobs/page.tsx.
3// Requirements:
4// - Fetch published jobs from Supabase ordered by is_featured DESC, published_at DESC
5// - Display as shadcn/ui Card grid: job title, company name + logo, location, salary range, job_type Badge
6// - Featured jobs have a gold border and 'Featured' Badge
7// - Add a Sheet sidebar for filters: job_type RadioGroup, salary range Slider, location Input
8// - Add a search Input at the top that filters by title or company name
9// - Each Card links to /jobs/[slug] for the full job detail
10// - Show total results count and a 'Post a Job' Button linking to /employer/post

Expected result: The jobs page displays a searchable, filterable grid of job listings with featured jobs highlighted at the top.

3

Create the job detail page with apply functionality

Build the individual job page showing full details and a quick-apply form. The apply form uploads resumes to Supabase Storage and creates an application record.

prompt.txt
1// Paste this prompt into V0's AI chat:
2// Create a job detail page at app/jobs/[slug]/page.tsx.
3// Requirements:
4// - Fetch the job by ID with company details from Supabase
5// - Show job title, company logo + name + website link, location, salary range, job_type Badge, posted date
6// - Show full job description in a formatted Card
7// - Add an 'Apply Now' Button that opens a Dialog with:
8// - applicant_name Input, email Input, cover_letter Textarea
9// - Resume file upload (PDF only, max 5MB) to Supabase Storage
10// - Submit Button with loading state
11// - Add a 'Save Job' Button (heart icon) that toggles saved_jobs
12// - Show related jobs from the same company or category at the bottom
13// - Server Action for the application submission that inserts into applications table

Pro tip: Use Supabase Storage with a private bucket for resumes. Generate signed URLs server-side when employers view applications — this keeps resume files secure and inaccessible to unauthorized users.

Expected result: The job detail page shows full listing information with a working apply Dialog that uploads resumes and creates applications.

4

Build the employer dashboard and posting flow

Create the employer-facing dashboard for managing listings and viewing applicants. Include a job posting form with a Stripe Checkout redirect for featured listings.

prompt.txt
1// Paste this prompt into V0's AI chat:
2// Create an employer dashboard at app/employer/dashboard/page.tsx.
3// Requirements:
4// - Fetch all jobs for the current user's company with application counts
5// - Display in a shadcn/ui Table: title, status Badge (draft/active/expired), applications count, is_featured Badge, published_at
6// - Each row has actions: Edit, View Applicants, Promote to Featured
7// - 'View Applicants' opens a Sheet with application list: name, email, resume link, status Badge (new/reviewed/shortlisted/rejected), Select to change status
8// - 'Promote to Featured' Button redirects to Stripe Checkout for $99 featured listing
9// - Add a 'Post New Job' Button linking to /employer/post
10// - Post page has a form: title, description (Textarea), location, salary_min, salary_max, job_type Select
11// - Server Action for job creation that inserts with status 'draft'

Expected result: The employer dashboard shows all listings with applicant counts, status management, and a featured listing purchase flow via Stripe.

5

Set up Stripe webhook for featured listing activation

Create the webhook handler that listens for Stripe checkout.session.completed events and automatically marks the corresponding job as featured. This is the critical piece that connects payment to listing promotion.

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 rawBody = 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 rawBody,
19 sig,
20 process.env.STRIPE_WEBHOOK_SECRET!
21 )
22 } catch (err) {
23 return NextResponse.json(
24 { error: 'Webhook signature verification failed' },
25 { status: 400 }
26 )
27 }
28
29 if (event.type === 'checkout.session.completed') {
30 const session = event.data.object as Stripe.Checkout.Session
31 const jobId = session.metadata?.job_id
32
33 if (jobId) {
34 await supabase
35 .from('jobs')
36 .update({
37 is_featured: true,
38 stripe_payment_id: session.payment_intent as string,
39 status: 'active',
40 })
41 .eq('id', jobId)
42 }
43 }
44
45 return NextResponse.json({ received: true })
46}

Expected result: After a successful Stripe Checkout payment, the webhook automatically marks the job as featured and active.

6

Deploy and register the Stripe webhook

Publish the job board to Vercel, then register the webhook URL in the Stripe Dashboard so payment events trigger the featured listing activation in production.

prompt.txt
1// Paste this prompt into V0's AI chat:
2// Create a Stripe Checkout session creator as a Server Action in app/employer/actions.ts.
3// Requirements:
4// - Export async function createFeaturedCheckout(jobId: string) with 'use server'
5// - Create a Stripe Checkout session with:
6// - mode: 'payment'
7// - line_items: one item for 'Featured Job Listing' at $99.00
8// - metadata: { job_id: jobId }
9// - success_url: '{origin}/employer/dashboard?featured=success'
10// - cancel_url: '{origin}/employer/dashboard?featured=cancelled'
11// - Return the session URL for redirect
12// - Use redirect() from next/navigation to send user to Stripe

Pro tip: After publishing, register your webhook URL in Stripe Dashboard at Developers > Webhooks > Add endpoint. Use your Vercel URL: https://yourdomain.vercel.app/api/webhooks/stripe. Select the checkout.session.completed event.

Expected result: The job board is live on Vercel. Featured listing payments are processed through Stripe Checkout and automatically activate via the webhook.

Complete code

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 rawBody = 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 rawBody,
19 sig,
20 process.env.STRIPE_WEBHOOK_SECRET!
21 )
22 } catch (err) {
23 console.error('Webhook verification failed:', err)
24 return NextResponse.json(
25 { error: 'Invalid signature' },
26 { status: 400 }
27 )
28 }
29
30 if (event.type === 'checkout.session.completed') {
31 const session = event.data.object as Stripe.Checkout.Session
32 const jobId = session.metadata?.job_id
33
34 if (jobId) {
35 const { error } = await supabase
36 .from('jobs')
37 .update({
38 is_featured: true,
39 stripe_payment_id: session.payment_intent as string,
40 status: 'active',
41 published_at: new Date().toISOString(),
42 expires_at: new Date(
43 Date.now() + 30 * 24 * 60 * 60 * 1000
44 ).toISOString(),
45 })
46 .eq('id', jobId)
47
48 if (error) console.error('Failed to update job:', error)
49 }
50 }
51
52 return NextResponse.json({ received: true })
53}

Customization ideas

Email notifications for new applications

Send employers an email via Resend whenever a new application is submitted, including the applicant's name and a link to their dashboard.

Company profiles with public pages

Add a /companies/[slug] page showing company info, open positions, and reviews — giving employers a branded presence on your job board.

Subscription-based employer plans

Replace one-time featured listing payments with monthly subscription plans (Basic: 5 postings, Pro: unlimited + featured) using Stripe Billing.

Job alerts and saved searches

Let job seekers save search criteria and receive email notifications when new matching jobs are posted, using a Vercel Cron Job for daily digest emails.

Common pitfalls

Pitfall: Using request.json() instead of request.text() in the Stripe webhook handler

How to avoid: Always use request.text() to get the raw body, then pass it directly to stripe.webhooks.constructEvent(rawBody, sig, webhookSecret).

Pitfall: Putting STRIPE_SECRET_KEY or STRIPE_WEBHOOK_SECRET with a NEXT_PUBLIC_ prefix

How to avoid: Store STRIPE_SECRET_KEY and STRIPE_WEBHOOK_SECRET in V0's Vars tab without any prefix. Only NEXT_PUBLIC_STRIPE_PUBLISHABLE_KEY should have the prefix.

Pitfall: Not validating the Stripe webhook signature before processing events

How to avoid: Always verify the webhook signature using stripe.webhooks.constructEvent() with the raw body and your STRIPE_WEBHOOK_SECRET before processing any event.

Pitfall: Calculating scores or filtering job results client-side

How to avoid: Use Supabase server-side queries with .ilike() for search, .eq() for filters, and .range() for pagination. The Server Component fetches only the needed results.

Best practices

  • Use Server Components for job listing and detail pages to get SEO benefits — search engines see fully rendered HTML with job titles and descriptions
  • Store all Stripe secret keys in V0's Vars tab without NEXT_PUBLIC_ prefix — only the publishable key needs the prefix
  • Use Supabase Storage private buckets for resume uploads and generate signed URLs when employers view applications
  • Add a unique constraint on job slugs for clean, SEO-friendly URLs like /jobs/senior-react-developer-acme
  • Use V0's Design Mode (Option+D) to adjust Card layouts, Badge colors, and filter sidebar width without spending credits
  • Set job expiration dates (expires_at) and filter them out in queries so stale listings do not appear to candidates
  • Use Stripe metadata to pass the job_id through the Checkout session so the webhook knows which job to update
  • Implement cursor-based pagination for the job listing page to handle thousands of listings efficiently

AI prompts to try

Copy these prompts to build this project faster.

ChatGPT Prompt

I'm building a job board with Next.js App Router, Supabase, and Stripe. I need help with the Stripe Checkout flow for featured listings. When an employer clicks 'Promote to Featured', I need to create a Checkout session with the job_id in metadata, redirect them to Stripe, and then handle the checkout.session.completed webhook to update the job's is_featured flag. Please write the Server Action for creating the session and the webhook handler at app/api/webhooks/stripe/route.ts using request.text() for signature verification.

Build Prompt

Create a complete employer job posting workflow: a form at app/employer/post/page.tsx with title Input, description Textarea, location Input, salary range (two number Inputs), job_type Select (Full-time, Part-time, Contract, Remote). On submit, use a Server Action to insert into the jobs table with status 'draft'. Add a preview step before submission showing the job as it will appear to candidates. Include validation with zod schema.

Frequently asked questions

How does the featured listing payment work?

When an employer clicks Promote to Featured, a Server Action creates a Stripe Checkout session with the job_id in metadata and redirects them to Stripe's hosted payment page. After payment, Stripe sends a webhook to your app, which verifies the signature and automatically updates the job to is_featured = true.

Can I use the free V0 plan for this project?

You can start on the free tier, but Premium ($20/month) is recommended. The job board requires multiple pages (listings, detail, employer dashboard, webhook handler) and the free tier's limited credits may not cover the full build.

How are resumes stored securely?

Resumes are uploaded to a Supabase Storage private bucket. They are not publicly accessible. When employers view applications, your server generates a signed URL that expires after one hour, ensuring only authorized users can download resume files.

Can I add email notifications when someone applies?

Yes. Add a Resend API call in the application submission Server Action. After inserting the application record, send an email to the employer with the applicant's name and a link to their dashboard. Store RESEND_API_KEY in the Vars tab without a NEXT_PUBLIC_ prefix.

How do I deploy the job board?

Click Share in V0, then Publish to Production. After deploying, register your Stripe webhook URL in the Stripe Dashboard at Developers > Webhooks. Use your production URL: https://yourdomain.vercel.app/api/webhooks/stripe and select checkout.session.completed.

Can I make job listing pages SEO-friendly?

Yes. Job detail pages use Server Components by default, so search engines see fully rendered HTML. Add metadata with generateMetadata() for dynamic page titles and descriptions. Use ISR with revalidate for fast cached pages that update when jobs change.

Can RapidDev help build a custom job board?

Yes. RapidDev has built over 600 apps including job boards and recruitment platforms with advanced features like applicant scoring, interview scheduling, and employer analytics. Book a free consultation to discuss your niche and get a production-ready platform.

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.