Best Boilerplates for Course Platforms in 2026
Building a custom online course platform is a major undertaking — video delivery, curriculum management, progress tracking, quizzes, certificates, payment processing, and a student dashboard. Most course creators use Teachable, Kajabi, or Thinkific and never need to build this.
The case for building is narrow but real: you're building a white-label LMS to sell to other businesses, you need features that existing platforms don't support (custom certification workflows, deep CRM integration, B2B training with SSO), or your monthly revenue is high enough that platform fees exceed the cost of custom development.
Build vs Buy: The Honest Decision Framework
| Monthly Revenue | Recommendation | Economics |
|---|---|---|
| $0–$2k | Gumroad (free) or Lemon Squeezy | Platform fees < dev cost |
| $2k–$10k | Teachable Free or Creator tier ($39/mo) | Best time-to-market |
| $10k–$50k | Kajabi ($149/mo) or Thinkific Pro ($99/mo) | Full featured, worth the fee |
| $50k+ | Custom platform | Dev investment pays back in 6-12 months |
| White-label LMS | Custom from day one | No platform supports multi-merchant LMS |
Building a custom course platform makes sense for:
- White-label LMS — selling the platform to other educators or businesses
- B2B corporate training — enterprise SSO, compliance tracking, manager dashboards
- Community-integrated learning — tight coupling between courses and a community platform
- Specialized vertical — medical education with CME credits, coding bootcamps with assessments
Best Foundation Starters
Payload CMS + Next.js — Best CMS-First Approach
Price: Free (MIT) | Stack: Next.js, Payload CMS, PostgreSQL, Stripe
Payload CMS is the best foundation for course platforms that have significant content management requirements. Instructors need to manage video lessons, written content, downloadable resources, and quizzes — a headless CMS with a content editor is better than building custom admin screens.
Payload gives you: a TypeScript-native headless CMS with a polished admin UI, collections for courses/modules/lessons, media management, and a GraphQL or REST API. Pair it with Next.js for the student-facing frontend and Stripe for payments.
Customization path:
- Define Course, Module, Lesson, Quiz collections in Payload
- Build student-facing pages in Next.js using Payload's content API
- Add enrollment tracking and progress in a PostgreSQL database
- Integrate Mux or Cloudflare Stream for video
Best for: Course platforms with non-technical instructors who need a good content editing experience.
Supastarter — Best SaaS Foundation
Price: $299+ | Stack: Next.js, Supabase, Stripe
Supastarter's multi-tenant architecture maps well to B2B training platforms — each "organization" becomes a company with their own employees/students, course assignments, and progress dashboards. Supabase handles real-time progress updates efficiently.
The auth system (Supabase Auth) supports SSO via SAML 2.0, which is required for enterprise B2B training where companies want employees to use their existing identity provider.
Best for: B2B training platforms with team management and enterprise SSO requirements.
Open edX — Best Enterprise LMS
Price: Free (AGPL) | Creator: MIT/Harvard | Stack: Django, React
Open edX is the most feature-complete open source LMS available — the platform that runs edX.org, MIT OpenCourseWare, and hundreds of enterprise installations. Features include video delivery, interactive problems, peer assessment, cohorts, certificates, discussion forums, analytics, and enterprise SSO.
The tradeoff: complexity and operational overhead. Open edX requires significant DevOps expertise to deploy and maintain. The Tutor project (https://docs.tutor.edly.io) simplifies Docker-based deployment considerably.
Best for: Enterprise or academic LMS deployments where feature completeness matters more than customization speed.
Custom Next.js + Stripe — Best Flexibility
Price: Free (dev cost) | Stack: Next.js, Prisma, Stripe
For course platforms with custom requirements — specialized progress tracking, proprietary assessment types, or unique UX — building directly on Next.js and Prisma provides maximum flexibility.
The core data model covers the standard LMS patterns:
// Prisma schema for course platform:
model Course {
id String @id @default(cuid())
title String
description String
price Decimal? // null = free
slug String @unique
published Boolean @default(false)
thumbnail String?
modules Module[]
enrollments Enrollment[]
instructor User @relation(fields: [instructorId], references: [id])
instructorId String
}
model Module {
id String @id @default(cuid())
title String
order Int
lessons Lesson[]
course Course @relation(fields: [courseId], references: [id])
courseId String
}
model Lesson {
id String @id @default(cuid())
title String
videoUrl String? // Mux or Cloudflare Stream URL
content String? // MDX content
duration Int? // Seconds
order Int
isFree Boolean @default(false) // Preview lessons
completions LessonCompletion[]
module Module @relation(fields: [moduleId], references: [id])
moduleId String
}
model Enrollment {
id String @id @default(cuid())
student User @relation(fields: [studentId], references: [id])
studentId String
course Course @relation(fields: [courseId], references: [id])
courseId String
completedAt DateTime?
createdAt DateTime @default(now())
@@unique([studentId, courseId])
}
model LessonCompletion {
id String @id @default(cuid())
student User @relation(fields: [studentId], references: [id])
studentId String
lesson Lesson @relation(fields: [lessonId], references: [id])
lessonId String
createdAt DateTime @default(now())
@@unique([studentId, lessonId])
}
Video Delivery
Never use YouTube or self-hosted video for a course platform. YouTube's recommendation algorithm will surface your competitors. Self-hosted video doesn't adapt to bandwidth. Use a professional video CDN:
| Provider | Storage | Playback | Best For |
|---|---|---|---|
| Mux | $0.015/min | $0.005/min viewed | Best DX, streaming, analytics |
| Cloudflare Stream | $5/1k min stored | $1/1k min viewed | Best price/performance |
| Bunny.net | $10/TB storage | $9/TB bandwidth | Best for high-volume |
| Vimeo Pro | Flat rate | Included | Simplest setup |
// Upload video to Mux (direct from browser):
import Mux from '@mux/mux-node';
const mux = new Mux();
// 1. Create upload URL (server-side):
const upload = await mux.video.uploads.create({
new_asset_settings: {
playback_policy: ['signed'], // Private lessons require signed URLs
mp4_support: 'standard', // Enable downloadable MP4 for offline
},
cors_origin: 'https://yourdomain.com',
});
// 2. Client uploads directly to Mux (not through your server):
// return upload.url to frontend — UpChunk or fetch PUT to this URL
// 3. Webhook: asset.ready → update lesson with playback ID
case 'video.asset.ready':
await db.lesson.update({
where: { muxUploadId: event.data.upload_id },
data: {
muxAssetId: event.data.id,
muxPlaybackId: event.data.playback_ids[0].id,
duration: Math.floor(event.data.duration),
}
});
break;
Progress Tracking and Certificates
// Track lesson completion and auto-issue certificate:
async function markLessonComplete(
studentId: string,
lessonId: string
): Promise<void> {
await db.lessonCompletion.upsert({
where: { studentId_lessonId: { studentId, lessonId } },
create: { studentId, lessonId },
update: {},
});
// Check if course is complete:
const lesson = await db.lesson.findUnique({
where: { id: lessonId },
include: { module: { include: { course: { include: { modules: { include: { lessons: true } } } } } } },
});
const course = lesson.module.course;
const allLessonIds = course.modules.flatMap(m => m.lessons.map(l => l.id));
const completedIds = await db.lessonCompletion.findMany({
where: { studentId, lessonId: { in: allLessonIds } },
select: { lessonId: true },
});
if (completedIds.length === allLessonIds.length) {
// Course complete — issue certificate:
await issueCertificate(studentId, course.id);
await db.enrollment.update({
where: { studentId_courseId: { studentId, courseId: course.id } },
data: { completedAt: new Date() }
});
}
}
Quiz and Assessment System
// Quiz schema:
model Quiz {
id String @id @default(cuid())
lessonId String
lesson Lesson @relation(fields: [lessonId], references: [id])
questions Question[]
}
model Question {
id String @id @default(cuid())
quizId String
quiz Quiz @relation(fields: [quizId], references: [id])
text String
type String // multiple_choice, true_false, short_answer
options Json? // Array of answer options
correctAnswer String // For auto-grading
points Int @default(1)
}
// Auto-grade multiple choice quiz:
async function gradeQuiz(
studentId: string,
quizId: string,
answers: Record<string, string>
): Promise<{ score: number; passed: boolean }> {
const questions = await db.question.findMany({ where: { quizId } });
let points = 0;
let totalPoints = 0;
for (const question of questions) {
totalPoints += question.points;
if (answers[question.id] === question.correctAnswer) {
points += question.points;
}
}
const score = Math.round((points / totalPoints) * 100);
const passed = score >= 70; // 70% pass threshold
await db.quizAttempt.create({
data: { studentId, quizId, score, passed, answers }
});
return { score, passed };
}
Pricing Models for Course Platforms
| Model | Implementation | Best For |
|---|---|---|
| One-time per course | Stripe PaymentIntent | Individual courses |
| Course bundle | Single price for multiple courses | Upsell strategy |
| Monthly subscription | Stripe Subscriptions with entitlements | All-access pass |
| Cohort-based | One-time + fixed enrollment period | Live/cohort programs |
| Free + Paid | Stripe + public lessons (isFree flag) | Lead generation |
Related Resources
For video delivery comparisons, see the UploadThing vs S3 vs Cloudflare R2 guide. For the SaaS billing infrastructure behind course subscriptions, see the best boilerplates for subscription billing.
Browse course platform and EdTech boilerplates at StarterPick — filter by CMS, video integration, and billing model.
Student Experience Features That Drive Course Completion
The technical infrastructure of a course platform is table stakes — what determines whether students actually complete your course (and tell others about it) is the learning experience layer. Completion rates for most online courses sit below 15%. Platforms that invest in engagement features meaningfully outperform that benchmark.
Progress persistence. Every lesson should record a completion event when the student reaches the end — either a video play-through to 90% or a "Mark as complete" button for text lessons. Progress should persist across devices. The minimal implementation is a LessonProgress table keyed on (userId, lessonId) with a completedAt timestamp. Display completion percentage in the course sidebar to give students a sense of forward momentum.
Lesson bookmarks and notes. Students who can annotate lessons are more engaged than those who passively consume content. A simple notes feature — a textarea attached to each lesson that auto-saves — increases time-on-platform and reduces "where was that?" support tickets. This is 2-4 hours to build on top of any boilerplate: a Note model with userId, lessonId, and content, plus a client component that debounce-saves on keystroke.
Certificates of completion. Certificates are a strong enrollment driver for professional courses. They are also straightforward to implement: once all lessons in a course are marked complete, generate a PDF certificate using a template (React PDF or Puppeteer), store it in S3 or Cloudflare R2, and email a download link. The certificate should include the student's name, course title, completion date, and a verification URL — a public URL that validates the certificate's authenticity without requiring login.
Community and Q&A. A discussion thread per lesson — even a simple one — reduces course abandonment. Students who get questions answered continue; students who get stuck silently drop off. The lightest-weight implementation is embedding a Discord link per course cohort. The next level is a native Q&A component (question + answer thread per lesson, upvoting, instructor-answered flag) using any of the realtime boilerplate patterns discussed earlier.
For course platforms that need community features as a first-class capability alongside learning, see the best SaaS boilerplates guide for starters with pre-built social and community infrastructure.
Protecting Course Content
Content protection is the security dimension most course platform boilerplates omit from their scaffolding. Unprotected video URLs can be downloaded and shared, bypassing your paywall entirely. Implementing content protection correctly from the start avoids a painful retrofit later.
Signed URLs for video. Never expose direct S3 or Cloudflare R2 URLs to enrolled students. Use signed URLs with short expiry windows (15-60 minutes). The student's browser requests the video URL from your API, the API verifies enrollment, generates a signed URL, and returns it. The signed URL expires before it can be meaningfully shared. Mux's Signed URLs feature handles this natively if you use Mux for video hosting.
Hotlink protection for static assets. Lesson PDFs, downloadable resources, and slide decks should be served through your application layer, not via direct CDN links. The same signed URL pattern applies: the student fetches the resource through an API endpoint that checks enrollment before generating the download URL.
DRM for premium video content. For high-value courses where video content is the core IP, Widevine + FairPlay DRM (available through Mux's DRM offering) prevents screen recording and download at the OS level. DRM adds cost and complexity; it is appropriate for professional certification courses and premium corporate training content, not typical online courses where the instruction relationship is the main value.
Enrollment verification in middleware. Every lesson page — server-rendered or client-rendered — should verify enrollment through middleware before rendering the content. The most common mistake is checking enrollment in the page component but not in the API route that serves the lesson content, leaving a direct API access path that bypasses the page-level check. Check enrollment in the API route that serves lesson data, not just in the UI component that renders it.
Browse course platform boilerplates at StarterPick — filter by CMS, video integration, and billing model.
See the best SaaS boilerplates guide for the full comparison including platforms with pre-built community features.
Read the best free open-source SaaS boilerplates guide for zero-cost LMS foundations.
Check out this boilerplate
View Payload CMS + Next.json StarterPick →