Skip to main content

Best Boilerplates for Course Platforms in 2026

·StarterPick Team
Share:

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 RevenueRecommendationEconomics
$0–$2kGumroad (free) or Lemon SqueezyPlatform fees < dev cost
$2k–$10kTeachable Free or Creator tier ($39/mo)Best time-to-market
$10k–$50kKajabi ($149/mo) or Thinkific Pro ($99/mo)Full featured, worth the fee
$50k+Custom platformDev investment pays back in 6-12 months
White-label LMSCustom from day oneNo 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:

ProviderStoragePlaybackBest For
Mux$0.015/min$0.005/min viewedBest DX, streaming, analytics
Cloudflare Stream$5/1k min stored$1/1k min viewedBest price/performance
Bunny.net$10/TB storage$9/TB bandwidthBest for high-volume
Vimeo ProFlat rateIncludedSimplest 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

ModelImplementationBest For
One-time per courseStripe PaymentIntentIndividual courses
Course bundleSingle price for multiple coursesUpsell strategy
Monthly subscriptionStripe Subscriptions with entitlementsAll-access pass
Cohort-basedOne-time + fixed enrollment periodLive/cohort programs
Free + PaidStripe + public lessons (isFree flag)Lead generation

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.

The SaaS Boilerplate Matrix (Free PDF)

20+ SaaS starters compared: pricing, tech stack, auth, payments, and what you actually ship with. Updated monthly. Used by 150+ founders.

Join 150+ SaaS founders. Unsubscribe in one click.