Skip to main content
RapidDev - Software Development Agency

How to Build Online education platform with V0

Build a full online course platform with V0 using Next.js, Supabase, Stripe, and shadcn/ui. Features structured courses with video lessons, interactive quiz modules, progress tracking, completion certificates via PDF generation, and Stripe-powered course purchases. Takes about 1-2 hours to complete.

What you'll build

  • Course catalog with Card grid, category filtering, and instructor profiles
  • Lesson viewer with sidebar navigation, video embedding, and quiz RadioGroup components
  • Progress tracking with completion percentages and lesson status indicators via shadcn/ui Progress
  • Stripe Checkout for course purchases with webhook-created enrollment records
  • PDF certificate generation on course completion using server-side rendering
  • Course syllabus with Accordion for expandable module and lesson tree
Book a free consultation
4.9Clutch rating
600+Happy partners
17+Countries served
190+Team members
Intermediate8 min read1-2 hoursV0 Premium or higherApril 2026RapidDev Engineering Team
TL;DR

Build a full online course platform with V0 using Next.js, Supabase, Stripe, and shadcn/ui. Features structured courses with video lessons, interactive quiz modules, progress tracking, completion certificates via PDF generation, and Stripe-powered course purchases. Takes about 1-2 hours to complete.

What you're building

Educators and training companies need a platform for hosting structured courses with video lessons, quizzes, and completion certificates. Commercial LMS platforms charge per-student fees that eat into margins.

V0 generates the course catalog, lesson viewer, and quiz components from prompts. Stripe via the Vercel Marketplace handles course purchases, and Supabase stores course content and progress tracking.

The architecture uses Server Components for SEO-friendly course landing pages, Stripe Checkout for payment, webhook handlers for enrollment creation, and Server Actions for lesson completion and quiz scoring.

Final result

An online education platform with course purchases, structured lessons, quizzes, progress tracking, and PDF completion certificates.

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)
  • Course content prepared (lesson text, video URLs, quiz questions)

Build steps

1

Set up the database schema for courses, modules, and lessons

Create the Supabase schema for the course structure with modules, lessons (video/text/quiz types), enrollments, progress tracking, and certificates.

prompt.txt
1// Paste this prompt into V0's AI chat:
2// Build an online education platform. Create a Supabase schema:
3// 1. courses: id (uuid PK), title (text), slug (text UNIQUE), description (text), instructor_id (uuid FK to auth.users), price_cents (int), stripe_price_id (text), thumbnail_url (text), status (text DEFAULT 'draft'), published_at (timestamptz)
4// 2. modules: id (uuid PK), course_id (uuid FK to courses), title (text), position (int)
5// 3. lessons: id (uuid PK), module_id (uuid FK to modules), title (text), type (text CHECK IN 'video','text','quiz'), content (jsonb), video_url (text), duration_minutes (int), position (int)
6// 4. enrollments: id (uuid PK), user_id (uuid FK to auth.users), course_id (uuid FK to courses), stripe_subscription_id (text), enrolled_at (timestamptz), completed_at (timestamptz)
7// 5. lesson_progress: user_id (uuid FK to auth.users), lesson_id (uuid FK to lessons), status (text DEFAULT 'not_started'), score (int), completed_at (timestamptz), PRIMARY KEY (user_id, lesson_id)
8// 6. certificates: id (uuid PK), enrollment_id (uuid FK to enrollments), issued_at (timestamptz), certificate_url (text)
9// Add RLS. Generate SQL and types.

Pro tip: Use V0's Stripe integration via Vercel Marketplace for course purchases and the Connect panel for Supabase setup in a single workflow.

Expected result: All tables created with proper foreign keys and RLS policies for student-scoped access.

2

Build the course catalog and landing pages

Create the public course pages with SEO-friendly Server Components showing course details, syllabus, and purchase buttons.

prompt.txt
1// Paste this prompt into V0's AI chat:
2// Create education platform pages:
3// 1. app/courses/page.tsx — course catalog with Card grid: thumbnail, title, instructor name, price, lesson count, rating. Add category Tabs and search Input.
4// 2. app/courses/[slug]/page.tsx — course landing page: hero with thumbnail, title, instructor Avatar, price, 'Enroll Now' Button. Syllabus section with Accordion showing modules and lessons. 'Enroll Now' creates a Stripe Checkout session via Server Action.
5// Use ISR with revalidate = 3600 for fast cached pages. Server Components for SEO.

Expected result: The catalog shows courses with ISR caching. Landing pages have a rich syllabus Accordion and Stripe Checkout enrollment.

3

Build the lesson viewer with progress tracking

Create the learning interface where enrolled students view lessons, take quizzes, and track their progress through the course.

prompt.txt
1// Paste this prompt into V0's AI chat:
2// Create the lesson viewer at app/learn/[courseId]/[lessonId]/page.tsx.
3// Requirements:
4// - Left sidebar: module/lesson tree with Accordion. Show completion status per lesson (checkmark icon, in-progress icon, or locked icon). Current lesson highlighted.
5// - Main area: lesson content based on type:
6// - video: embedded video player with video_url
7// - text: rendered markdown/rich text content from jsonb
8// - quiz: RadioGroup for multiple choice, submit Button, score display
9// - 'Mark as Complete' Button for video/text lessons
10// - Quiz auto-completes on submit with score tracking
11// - Progress bar at top showing course completion percentage
12// - Navigation Buttons: Previous Lesson, Next Lesson
13// - Server Action for marking lessons complete and submitting quiz answers
14// - On last lesson complete, check if all lessons done and show celebration + certificate generation link

Expected result: The lesson viewer shows content with sidebar navigation, progress tracking, and quiz submission with scoring.

4

Set up Stripe webhook and certificate generation

Create the webhook handler for enrollment creation on purchase and the certificate generation endpoint that runs when a course is completed.

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(process.env.SUPABASE_URL!, process.env.SUPABASE_SERVICE_ROLE_KEY!)
7
8export async function POST(req: NextRequest) {
9 const rawBody = await req.text()
10 const sig = req.headers.get('stripe-signature')!
11
12 let event: Stripe.Event
13 try {
14 event = stripe.webhooks.constructEvent(rawBody, sig, process.env.STRIPE_WEBHOOK_SECRET!)
15 } catch {
16 return NextResponse.json({ error: 'Invalid signature' }, { status: 400 })
17 }
18
19 if (event.type === 'checkout.session.completed') {
20 const session = event.data.object as Stripe.Checkout.Session
21 const { user_id, course_id } = session.metadata || {}
22
23 if (user_id && course_id) {
24 await supabase.from('enrollments').insert({
25 user_id,
26 course_id,
27 enrolled_at: new Date().toISOString(),
28 })
29 }
30 }
31
32 return NextResponse.json({ received: true })
33}

Expected result: Successful course purchases automatically create enrollment records. Students can immediately access course content.

5

Add quiz scoring and deploy

Build the quiz submission logic that scores answers server-side and the completion check that triggers certificate generation. Then deploy.

prompt.txt
1// Paste this prompt into V0's AI chat:
2// Add quiz and completion features:
3// 1. Server Action submitQuiz: receives attempt answers, fetches correct answers from lessons.content jsonb, computes score server-side (NEVER client-side to prevent cheating), stores in lesson_progress with score
4// 2. Server Action completeLesson: marks lesson as completed, checks if ALL lessons in course are completed. If yes, triggers certificate generation.
5// 3. Certificate generation: POST to /api/certificates/generate with enrollment_id. Generate a simple PDF with course title, student name, completion date, and a certificate ID. Upload to Supabase Storage and store URL in certificates table.
6// 4. Show certificate download Button on completed courses in the student dashboard
7// 5. Student dashboard at app/dashboard/page.tsx showing enrolled courses with Progress bars and certificate links

Pro tip: Use ISR with revalidate = 3600 for course landing pages so they load from the CDN edge but update within an hour when content changes.

Expected result: Quizzes score server-side, completion triggers certificates, and the student dashboard shows progress. The app is deployed.

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, sig, process.env.STRIPE_WEBHOOK_SECRET!
19 )
20 } catch {
21 return NextResponse.json({ error: 'Invalid sig' }, { status: 400 })
22 }
23
24 if (event.type === 'checkout.session.completed') {
25 const session = event.data.object as Stripe.Checkout.Session
26 const meta = session.metadata || {}
27
28 if (meta.user_id && meta.course_id) {
29 const { error } = await supabase.from('enrollments').insert({
30 user_id: meta.user_id,
31 course_id: meta.course_id,
32 enrolled_at: new Date().toISOString(),
33 })
34
35 if (error) console.error('Enrollment insert failed:', error)
36 }
37 }
38
39 return NextResponse.json({ received: true })
40}

Customization ideas

Discussion forums per lesson

Add a comments section below each lesson where students ask questions and instructors reply.

Drip content release

Unlock modules on a weekly schedule after enrollment date instead of releasing all content at once.

Instructor analytics

Build a dashboard showing enrollment trends, lesson completion rates, quiz performance, and revenue per course.

Subscription-based access

Replace one-time purchases with monthly subscriptions using Stripe Billing for all-access passes.

Common pitfalls

Pitfall: Scoring quizzes on the client side

How to avoid: Score quizzes in a Server Action. The client sends answer IDs; the server fetches correct answers from the database, computes the score, and stores results.

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

How to avoid: Use request.text() and pass the raw body to stripe.webhooks.constructEvent().

Pitfall: Not checking enrollment before showing lesson content

How to avoid: Check the enrollments table in the lesson viewer Server Component. If no active enrollment exists, redirect to the course landing page with a purchase prompt.

Best practices

  • Score quizzes server-side in Server Actions — never expose correct answers to the client
  • Use ISR with revalidate = 3600 for course landing pages for fast CDN-cached loads
  • Check enrollment status in the lesson viewer Server Component before rendering any content
  • Use Stripe Checkout metadata to pass user_id and course_id through the payment flow
  • Always use request.text() for Stripe webhook signature verification
  • Use V0's Design Mode (Option+D) to adjust course Card layouts and lesson viewer spacing without spending credits
  • Store quiz questions and answers in the lessons.content JSONB field for flexible content types
  • Generate certificates server-side to avoid shipping heavy PDF libraries to the client

AI prompts to try

Copy these prompts to build this project faster.

ChatGPT Prompt

I'm building an online education platform with Next.js App Router, Supabase, and Stripe. When a student completes all lessons in a course, I need to generate a PDF certificate with the course title, student name, and completion date. Please write the API route at app/api/certificates/generate/route.ts that generates a simple certificate, uploads it to Supabase Storage, and stores the URL in the certificates table.

Build Prompt

Create a quiz component that renders questions from a JSONB array. Each question has text, type (multiple_choice/true_false), options array, and correct_answer. Show questions one at a time with RadioGroup for answers, Progress bar for question progress, and Previous/Next/Submit Buttons. On submit, POST answers to a Server Action that scores them server-side and returns the results. Show a score Card with correct/incorrect breakdown.

Frequently asked questions

How does course enrollment work?

Students click 'Enroll Now' which creates a Stripe Checkout session. After payment, the webhook creates an enrollment record linking the user to the course. The student can immediately access all lessons.

Can I add video lessons?

Yes. Lessons with type 'video' display an embedded video player. Store video URLs from YouTube, Vimeo, or any host in the video_url field. The lesson viewer renders the appropriate player.

How are quizzes scored?

Students submit their answers via a Server Action. The server fetches correct answers from the database, computes the score, and stores results. Correct answers are never sent to the browser to prevent cheating.

Do I need a paid V0 plan?

Premium ($20/month) is recommended. The education platform has multiple complex pages that require several prompts to build.

How are certificates generated?

When all lessons are completed, a Server Action triggers the certificate API which generates a PDF server-side, uploads it to Supabase Storage, and stores the URL. Students download from their dashboard.

How do I deploy the platform?

Click Share in V0, then Publish to Production. Set Stripe and Supabase keys in the Vars tab. Register the webhook URL in Stripe Dashboard for checkout.session.completed.

Can RapidDev help build a custom education platform?

Yes. RapidDev has built over 600 apps including learning management systems with video hosting, live classes, and analytics. Book a free consultation to discuss your 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.