Breaking the Vertical List: Implementing Custom Course Formats in Moodle
Stop relying on the default vertical scroll in Moodle. Learn how to use the Course Format API to build custom grids, tabs, and dashboards that improve student navigation.
06 Jul 2025, 03:23 UTC

The Problem with the Infinite Scroll
By default, Moodle organizes content in a linear, vertical fashion—either by Topic or by Week. While functional, this "long page" approach often leads to cognitive overload for students and a tedious scrolling experience for instructors. When a course grows to 15+ sections, the interface becomes a barrier rather than a guide.
The solution isn't just adding CSS to the theme; it is implementing a custom Course Format. By leveraging Moodle's Course Format API, you can change how the system retrieves and renders course sections, transforming a vertical list into a grid, a tabbed interface, or a dashboard without altering the underlying course data.
How the Course Format API Works
Moodle treats course layouts as plugins located in the /course/format/ directory. Each format is a PHP class that extends the base course format logic. The core mechanism revolves around the get_course_content() method, which determines which modules (activities and resources) are displayed and in what order.
Because the format is a setting tied to the course record in the database, you can switch a course from "Topics" to a "Custom Grid" instantly. This means you don't have to migrate activities or rebuild the course structure; you are simply changing the presentation layer of the course content.
Building a Custom Layout: A Practical Approach
To create a custom format, you must define a plugin structure that Moodle recognizes. A minimal plugin requires a version.php for installation and a lib.php to handle the rendering logic.
Example: Adding a Custom Setting
One of the most powerful features of the API is the ability to add custom configuration options that appear directly in the Course Settings page. This allows instructors to toggle layout features (like "Enable Grid View") without touching code.
// In /course/format/customgrid/lib.php
function course_format_customgrid_get_options() {
global $CFG;
return array(
'show_grid' => new admin_setting_configselect(
'Enable Grid Layout',
'Choose whether to display sections as a grid or list',
'',
'show_grid',
array('1' => 'Grid', '0' => 'List')
),
);
}Implementation Steps
- Directory Setup: Create a folder at
/course/format/customgrid. - Permissions: Ensure the web server has read permissions for the new directory.
- Registration: Run the Moodle upgrade process (Site Administration > Notifications) to register the new plugin.
- Assignment: Navigate to a specific course's Settings > Course Format and select "Custom Grid" from the dropdown.
Performance and Compatibility Trade-offs
Custom formats offer flexibility, but they introduce specific engineering risks:
- The Render Loop: Avoid performing complex database queries inside the rendering method. Since this method runs every time a course page loads, inefficient SQL can significantly increase Page Load Time (PLT).
- Block Interference: Many Moodle blocks (like the "Latest Announcements" or "Completion Progress" blocks) expect a standard section layout. If your custom format removes standard HTML IDs or classes from the section containers, these blocks may fail to render or lose their styling.
- API Drift: Moodle's core API evolves. A custom format written for Moodle 4.x may require updates to its
get_course_contentimplementation when upgrading to a newer major version.
Verifying the Implementation
To confirm your custom format is operating correctly, perform these three checks:
- Admin Visibility: Check Site Administration > Plugins > Course formats. Your plugin should appear in the list of installed formats.
- Database State: Query the
mdl_coursetable. Theformatcolumn for your test course should match the folder name of your plugin (e.g.,customgrid). - Frontend Inspection: Use browser developer tools to ensure your custom CSS is loading and that the
#region-maincontainer is correctly wrapping your new layout.
0 replies
A thoughtful contribution can make all the difference. Be the first to share one.