Skip to main content
RapidDev - Software Development Agency
bubble-tutorial

How to build a learning management system in Bubble.io: Step-by-Step Guide

A learning management system in Bubble requires Data Types for courses, modules, lessons, quizzes, enrollments, and progress tracking. This tutorial covers the complete LMS architecture from course creation to student enrollment, lesson delivery with video hosting, progress tracking, and certificate generation.

What you'll learn

  • How to design a course data structure with modules, lessons, and quizzes
  • How to build enrollment and progress tracking workflows
  • How to deliver lessons with embedded video and text content
  • How to generate completion certificates
Book a free consultation
4.9Clutch rating
600+Happy partners
17+Countries served
190+Team members
Beginner6 min read40-50 minGrowth plan+ (video hosting and backend workflows benefit from higher plans)March 2026RapidDev Engineering Team
TL;DR

A learning management system in Bubble requires Data Types for courses, modules, lessons, quizzes, enrollments, and progress tracking. This tutorial covers the complete LMS architecture from course creation to student enrollment, lesson delivery with video hosting, progress tracking, and certificate generation.

Overview: Building a Learning Management System in Bubble

An LMS is a complex but highly valuable application — and one of Bubble's strongest use cases given its data-rich, multi-role nature. This tutorial walks you through building a complete LMS with course management, student enrollment, lesson delivery, progress tracking, quizzes, and certificates.

Prerequisites

  • A Bubble app on Growth plan or above
  • Basic experience with Data Types, workflows, and Repeating Groups
  • A video hosting service account (YouTube, Vimeo, or Loom)

Step-by-step guide

1

Design the LMS data structure

Create these Data Types. Course: title (text), description (text), instructor (User), thumbnail (image), price (number), published (yes/no), category (Option Set). Module: course (Course), title (text), order (number). Lesson: module (Module), title (text), content (text — rich text HTML), video_url (text), order (number), duration_minutes (number). Enrollment: course (Course), student (User), enrolled_date (date), completed (yes/no), completion_date (date). LessonProgress: enrollment (Enrollment), lesson (Lesson), completed (yes/no), completed_date (date).

Expected result: A hierarchical data structure: Course → Modules → Lessons with enrollment and progress tracking.

2

Build the course catalog and enrollment flow

Create a courses page with a Repeating Group showing published courses. Each cell displays the thumbnail, title, instructor name, price, and an Enroll button. The Enroll button workflow creates an Enrollment record linking the current user to the course. For paid courses, redirect to Stripe Checkout first, then create the enrollment on successful payment via a webhook.

Expected result: Students can browse courses and enroll for free or paid access.

3

Create the course player with lesson navigation

Create a page called course-player with page type Course. Show a sidebar with all Modules (Repeating Group sorted by order) and nested Lessons within each module. The main content area shows the current lesson — embed the video using an HTML element with an iframe pointing to the video_url, and display the lesson content below using an HTML element for rich text. Use a custom state to track the current lesson.

Pro tip: Use a Group with type Lesson to display the current lesson content, and update it by changing the data source when the user clicks a lesson in the sidebar.

Expected result: A course player with lesson navigation sidebar and main content area showing video and text.

4

Track lesson progress and course completion

When a student finishes a lesson (clicks a Mark as Complete button or watches the video to the end), create a LessonProgress record with completed = yes. After marking a lesson complete, check if all lessons in the course have progress records — if so, update the Enrollment to completed = yes and set the completion_date. Display progress visually using a progress bar (completed lessons / total lessons * 100).

Expected result: Student progress is tracked per lesson, and course completion is detected automatically.

5

Generate completion certificates

When an enrollment is marked as completed, create a certificate page that displays the student name, course title, completion date, and instructor name in a styled layout. Use the page as a printable certificate by styling it with a border, certificate-style fonts, and the course branding. Add a Print button that triggers window.print() via a Run JavaScript action. Alternatively, use a PDF generation plugin to create downloadable certificates.

Expected result: Students receive a printable or downloadable certificate upon course completion.

Complete working example

Workflow summary
1LMS ARCHITECTURE SUMMARY
2=========================
3
4DATA TYPES:
5 Course: title, description, instructor (User), thumbnail, price, published, category
6 Module: course (Course), title, order (number)
7 Lesson: module (Module), title, content (rich text), video_url, order, duration_minutes
8 Enrollment: course (Course), student (User), enrolled_date, completed (yes/no), completion_date
9 LessonProgress: enrollment (Enrollment), lesson (Lesson), completed, completed_date
10 Certificate: enrollment (Enrollment), issued_date, certificate_id (text)
11
12PAGES:
13 courses catalog with enrollment
14 course-player (type: Course) lesson viewer with sidebar nav
15 certificate (type: Enrollment) printable certificate
16 instructor-dashboard create/manage courses
17
18ENROLLMENT WORKFLOW:
19 Free: Create Enrollment (student=Current User, course, enrolled_date=now)
20 Paid: Stripe Checkout on success webhook Create Enrollment
21
22LESSON PROGRESS:
23 Mark Complete button:
24 1. Create LessonProgress (enrollment, lesson, completed=yes)
25 2. Check: Search LessonProgress (enrollment, completed=yes) count
26 = Search Lessons (course) count?
27 3. If yes Make Changes to Enrollment: completed=yes, completion_date=now
28 4. If yes Create Certificate
29
30PROGRESS BAR:
31 Width: (completed lessons / total lessons * 100)%
32 Text: "X of Y lessons completed"
33
34SIDEBAR NAVIGATION:
35 RG Modules (course, sorted by order):
36 Nested RG Lessons (module, sorted by order):
37 Conditional: green checkmark when LessonProgress exists and completed=yes
38 Click Set state: current_lesson = This Lesson

Common mistakes when building a learning management system in Bubble.io: Step-by-Step Guide

Why it's a problem: Not nesting modules and lessons properly in the data structure

How to avoid: Use separate Data Types for Course, Module, and Lesson with parent references, and sort by an order field

Why it's a problem: Tracking completion at the course level only

How to avoid: Create a LessonProgress record for each completed lesson so students can track their progress and resume

Why it's a problem: Hosting videos directly in Bubble

How to avoid: Host videos on YouTube (unlisted), Vimeo, or Loom and embed them via iframe in an HTML element

Best practices

  • Use a hierarchical data structure: Course → Module → Lesson for clear organization
  • Track progress at the lesson level for accurate completion tracking
  • Host videos externally (YouTube, Vimeo) and embed via iframe to save storage
  • Sort modules and lessons by an order field for consistent navigation
  • Add a progress bar showing percentage completion on the course player page
  • Generate certificates automatically when all lessons are marked complete

Still stuck?

Copy one of these prompts to get a personalized, step-by-step explanation.

ChatGPT Prompt

I want to build a learning management system in Bubble.io with courses, modules, lessons, video content, progress tracking, and completion certificates. Can you outline the complete data structure and key workflows?

Bubble Prompt

Build an LMS for my app. I need courses with modules and lessons, student enrollment, video lesson delivery, progress tracking per lesson, and automatic certificate generation on course completion.

Frequently asked questions

How do I embed videos from YouTube or Vimeo?

Add an HTML element and paste an iframe tag: <iframe src="https://www.youtube.com/embed/VIDEO_ID" width="100%" height="400" frameborder="0" allowfullscreen></iframe>. Make the src dynamic using the lesson's video_url field.

Can I restrict access to paid course content?

Yes. On the course player page, check if the current user has an Enrollment for the course. If not, redirect to the enrollment page or show a paywall message.

How do I handle multiple instructors?

Add an instructors field (list of Users) to the Course Data Type. Use Privacy Rules to give instructors edit access only to their own courses.

Can students leave reviews?

Yes. Create a Review Data Type with course (Course), student (User), rating (number), comment (text). Display reviews on the course catalog page.

Is Bubble suitable for a large-scale LMS?

For small to medium platforms (hundreds to low thousands of students), yes. For enterprise LMS with thousands of concurrent users, advanced reporting, and SCORM compliance, you may need hybrid architecture. RapidDev can help build and scale LMS platforms in Bubble.

Can I add downloadable resources to lessons?

Yes. Add a resources field (file, list) to the Lesson Data Type. Display download links below the lesson content using a Repeating Group of the resource list.

RapidDev

Talk to an Expert

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

Book a free consultation

Need help with your project?

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.