# How to build an eLearning platform in Bubble.io: Step-by-Step Guide

- Tool: Bubble
- Difficulty: Beginner
- Time required: 35-45 min
- Compatibility: All Bubble plans (Growth plan+ recommended for video hosting and backend workflows)
- Last updated: March 2026

## TL;DR

Building an e-learning platform in Bubble involves creating courses with video lessons, inline quizzes, completion tracking, discussion forums, and certificate generation. This tutorial covers the database schema for courses, modules, and lessons, the student-facing learning interface, progress tracking workflows, quiz grading logic, and automated certificate creation on course completion.

## Overview: Building an E-Learning Platform in Bubble

This tutorial walks you through building a student-facing e-learning platform in Bubble. You will create a structured course system with modules and lessons, embed video content, track student progress, add quizzes for knowledge checks, implement discussion forums per course, and generate completion certificates — all without writing code.

## Before you start

- A Bubble app with user authentication configured
- A video hosting account (Vimeo, YouTube, or Wistia) for lesson videos
- Familiarity with Bubble Data Types, Repeating Groups, and workflows
- Basic understanding of Bubble custom states

## Step-by-step guide

### 1. Design the course database schema

In the Data tab, create these Data Types: (1) Course — fields: title (text), description (text), instructor (User), thumbnail (image), category (Option Set: CourseCategory), is_published (yes/no), price (number). (2) Module — fields: course (Course), title (text), order_index (number), description (text). (3) Lesson — fields: module (Module), title (text), order_index (number), video_url (text), content (text), duration_minutes (number). (4) Quiz — fields: lesson (Lesson), question (text), options (list of texts), correct_answer_index (number). (5) Enrollment — fields: student (User), course (Course), enrolled_date (date), completed_date (date), progress_percent (number). (6) LessonProgress — fields: enrollment (Enrollment), lesson (Lesson), completed (yes/no), completed_date (date), quiz_score (number). (7) Discussion — fields: course (Course), author (User), message (text), parent_discussion (Discussion).

**Expected result:** Your database supports a complete course structure with modules, lessons, quizzes, enrollment tracking, and discussions.

### 2. Build the course catalog and enrollment flow

Create a 'courses' page with a Repeating Group showing published courses (Do a Search for Courses where is_published = yes). Display thumbnail, title, instructor name, category, and price. Link each cell to a 'course-detail' page (type = Course). On the detail page, show the course description, module list (Repeating Group of Modules where course = Current Page Course, sorted by order_index), and an Enroll button. The Enroll workflow: (1) Create an Enrollment with student = Current User, course = Current Page Course, progress_percent = 0. (2) Navigate to the first lesson of the first module.

**Expected result:** Students can browse the course catalog, view course details with module outlines, and enroll to start learning.

### 3. Create the lesson viewing interface

Create a 'lesson' page (type = Lesson). At the top, embed the video using an HTML element with an iframe pointing to the lesson's video_url. Below the video, display the lesson content text. On the left sidebar, show the course module structure with a nested Repeating Group: outer RG for Modules, inner RG for Lessons within each module. Highlight the current lesson. Add checkmarks next to completed lessons by checking if a LessonProgress exists where enrollment's student = Current User and lesson = Current Cell's Lesson and completed = yes. Add Previous and Next buttons that navigate to the adjacent lesson based on order_index.

> Pro tip: Use a custom state on the page to track the current enrollment — fetch it once on page load with Do a Search for Enrollments where student = Current User and course = Current Lesson's Module's Course, and store it in the state for fast access throughout the page.

**Expected result:** Students see the lesson video, content text, and course navigation sidebar with progress indicators.

### 4. Implement progress tracking and quizzes

Add a Mark as Complete button below the lesson content. The workflow: (1) Create a LessonProgress with completed = yes if one does not already exist, or update the existing one. (2) Calculate new progress_percent on the Enrollment: count of completed LessonProgress records divided by total lessons in the course, multiplied by 100. (3) Update the Enrollment's progress_percent field. For quizzes, when a lesson has associated Quiz records, display them after the video. Show the question, answer options as clickable buttons, and check the selected answer against correct_answer_index. Store the quiz_score on the LessonProgress. Only allow Mark as Complete if the quiz score meets a minimum threshold (e.g., 80%).

**Expected result:** Students can mark lessons complete, take quizzes, and see their overall course progress percentage updated in real time.

### 5. Add a discussion forum per course

On the course detail page, add a Discussion tab. Display a Repeating Group of Discussions where course = Current Page Course and parent_discussion is empty (top-level posts), sorted by Created Date descending. Inside each cell, show the author, message, date, and a Reply button. Replies are loaded in a nested Repeating Group where parent_discussion = Current Cell's Discussion. The Reply button shows a text area and Submit button that creates a new Discussion with parent_discussion set to the current post. Add a New Discussion form at the top for starting new threads.

**Expected result:** Students and instructors can start discussion threads and reply to posts within each course.

### 6. Generate certificates on course completion

When an Enrollment's progress_percent reaches 100, trigger a workflow to generate a certificate. Create a Certificate Data Type with fields: enrollment (Enrollment), certificate_number (text, auto-generated), issued_date (date). The workflow creates the Certificate record and updates the Enrollment's completed_date. For the visual certificate, create a 'certificate' page showing the student name, course title, completion date, and certificate number in a designed layout. Use Bubble's built-in ability to convert a page to PDF or use a plugin like PDF Conjurer to generate a downloadable certificate. Add a View Certificate button on the student dashboard.

**Expected result:** Students automatically receive a certificate when they complete all lessons, viewable and downloadable from their dashboard.

## Complete code example

File: `Workflow summary`

```text
E-LEARNING PLATFORM ARCHITECTURE
==================================

DATA TYPES:
  Course:
    - title, description, instructor (User)
    - thumbnail (image), category, price
    - is_published (yes/no)

  Module:
    - course (Course), title, order_index (number)

  Lesson:
    - module (Module), title, order_index (number)
    - video_url (text), content (text)
    - duration_minutes (number)

  Quiz:
    - lesson (Lesson), question (text)
    - options (list of texts)
    - correct_answer_index (number)

  Enrollment:
    - student (User), course (Course)
    - enrolled_date, completed_date
    - progress_percent (number)

  LessonProgress:
    - enrollment (Enrollment), lesson (Lesson)
    - completed (yes/no), completed_date
    - quiz_score (number)

  Discussion:
    - course (Course), author (User)
    - message (text)
    - parent_discussion (Discussion)

  Certificate:
    - enrollment (Enrollment)
    - certificate_number (text)
    - issued_date (date)

KEY WORKFLOWS:
  Enroll in Course:
    → Create Enrollment (progress: 0%)
    → Navigate to first lesson

  Mark Lesson Complete:
    → Create/update LessonProgress (completed: yes)
    → Calculate: completed lessons / total lessons * 100
    → Update Enrollment progress_percent
    → If 100%: trigger certificate generation

  Quiz Submission:
    → Compare selected answer to correct_answer_index
    → Store quiz_score on LessonProgress
    → Only allow completion if score >= 80%

  Generate Certificate:
    → Create Certificate record
    → Set Enrollment completed_date
    → Send congratulations email

PAGES:
  courses      — catalog with search/filter
  course-detail — overview, modules, enrollment
  lesson       — video player, content, quiz, nav
  certificate  — printable/downloadable certificate
  dashboard    — enrolled courses, progress bars
```

## Common mistakes

- **Hosting videos directly in Bubble's file storage** — Bubble's storage is not optimized for video streaming — large video files consume storage quickly and load slowly without adaptive bitrate streaming Fix: Host videos on Vimeo, YouTube (unlisted), or Wistia and embed them using iframe URLs in your Bubble HTML element
- **Calculating progress with a search on every page load** — Counting completed LessonProgress records on every lesson page view consumes workload units and slows the experience Fix: Store progress_percent as a field on Enrollment and update it only when a lesson is marked complete
- **Not enforcing lesson order or prerequisites** — Without order enforcement, students can skip to the final lesson and mark the course complete without learning the material Fix: Add an Only when condition on the Mark Complete button that checks if all previous lessons in the module are completed

## Best practices

- Host videos on a dedicated platform like Vimeo or Wistia for adaptive streaming quality
- Store progress_percent on the Enrollment record to avoid recalculating on every page load
- Use order_index fields on Modules and Lessons to control display and navigation order
- Cache the current Enrollment in a custom state on page load for fast reference throughout the lesson page
- Require quiz completion with a minimum score before marking lessons complete
- Paginate discussion threads to avoid loading hundreds of posts at once
- Use backend workflows for certificate generation to ensure they complete reliably

## Frequently asked questions

### What is the best way to embed videos in Bubble?

Host videos on Vimeo (Pro for privacy controls), YouTube (unlisted), or Wistia. Embed them using an HTML element with an iframe tag pointing to the video's embed URL. This gives you adaptive streaming without consuming Bubble storage.

### Can I charge for courses in Bubble?

Yes. Use the Stripe plugin to charge students at enrollment. Store the payment status on the Enrollment record and only grant access to course content when payment is confirmed.

### How do I generate downloadable certificates?

Create a certificate page with the student name, course title, and date laid out attractively. Use a plugin like PDF Conjurer to convert the page to a downloadable PDF, or let students print the page directly from their browser.

### Can I track how long students spend on each lesson?

Yes. Record the timestamp when a student opens a lesson page and when they leave or mark complete. Store the difference as time_spent on the LessonProgress record. Use the Do when condition is true event to detect page unload.

### Can RapidDev help build an e-learning platform in Bubble?

Yes. RapidDev can build a complete e-learning platform including course management, video integration, quiz engines, progress tracking, certificate generation, and payment processing — tailored to your educational content and audience.

### How do I handle different content types beyond video?

Add a content_type field to Lessons (Option Set: Video, Text, PDF, Interactive). Use conditional visibility to show the appropriate viewer element based on content_type — an HTML iframe for videos, a Text element for text content, or a file download link for PDFs.

---

Source: https://www.rapidevelopers.com/bubble-tutorial/build-an-elearning-platform-in-bubble
© RapidDev — https://www.rapidevelopers.com/bubble-tutorial/build-an-elearning-platform-in-bubble
