Customizing Moodle Course Layouts via the Course Format API
Learn how to use the Moodle Course Format API to replace the default 'long scroll' layout with custom grids or dashboards using PHP and Mustache templates.
10 Feb 2026, 11:55 UTC

The Problem: The 'Long Scroll' Fatigue
By default, Moodle organizes content into a linear list of topics or weeks. While functional, this often results in the "scroll of death," where students must navigate a massive vertical page to find a specific resource. When a course requires a non-linear structure—such as a grid of modules, a competency-based map, or a dashboard-style layout—simply rearranging activities isn't enough. You need to change how Moodle renders the course content entirely.
The solution is to develop a custom course_format plugin. Instead of hacking the theme or using CSS to hide elements, you can hook into Moodle's Course Format API to redefine the structural logic of the course home page.
How the Course Format API Works
Moodle treats course formats as plugins located in the /course/format/ directory. Every format extends a base class that tells Moodle how to handle course sections (the containers) and course modules (the actual activities like quizzes or files).
The core of your custom layout lives in the get_course_content() method. This method is the primary engineering hook; it is called when a user visits the course home page. Within this method, you access the course data and determine which sections to display and in what order.
The Role of Mustache Templates
Modern Moodle versions (3.x and 4.x) separate business logic from presentation using Mustache templates (.mustache files). Rather than echoing HTML directly in PHP, your plugin should prepare a data object and pass it to a template. This allows you to change the visual layout—switching from a list to a grid, for example—without rewriting the underlying PHP logic.
Implementing a Custom Layout: A Worked Example
To create a basic custom format, you must establish the plugin structure in /course/format/yourformatname/. You will need a version.php for installation and a lang/en/course_format_yourformatname.php file for the display name.
Example: Creating a Simplified Grid Logic
In your format's main class, you can override the rendering logic to group modules differently. Below is a conceptual implementation of how to prepare data for a custom grid layout:
// Inside /course/format/yourformatname/lib.php
class format_yourformatname extends course_format {
public function get_course_content() {
$course = $this->course;
$sections = $course->get_sections();
$content_data = [];
foreach ($sections as $section) {
// Only include sections that actually have activities
if (!empty($section->modules)) {
$content_data[] = [
'title' => $section->name,
'modules' => $section->modules,
'id' => $section->id
];
}
}
// Pass the data to a Mustache template for grid rendering
return $this->page->get_renderer()->render_from_template(
'format_yourformatname/course_grid',
['sections' => $content_data]
);
}
}Deployment and Verification
- Upload the plugin folder to
/course/format/. - Log in as an Administrator and go to Site Administration > Notifications to trigger the plugin installation.
- Navigate to a specific course, go to Course Settings > Course Format, and select your new format from the dropdown menu.
- Verification: Inspect the page source to ensure the HTML structure matches your Mustache template and verify that all course modules (CMs) are linked to their correct IDs.
Trade-offs and Performance Risks
While custom formats provide immense flexibility, they introduce specific engineering risks:
- Database Overhead: If you perform complex database queries inside the
foreachloop of course sections, page load times will spike as the number of activities grows. Always fetch required data in bulk before entering the loop. - Theme Compatibility: Many third-party Moodle themes assume the standard "Topics" layout. A custom format may strip away expected CSS classes, causing your layout to look broken in certain themes. You should include a dedicated CSS file within your plugin to ensure visual consistency.
- Upgrade Friction: The Course Format API is relatively stable, but major Moodle releases occasionally change how the renderer handles course content. Custom formats require regression testing after every major version upgrade.
Closing Action
If your users are struggling with navigation, start by auditing your current course structure. If the "Topics" format cannot be solved with simple CSS, implement a custom course_format plugin. Focus on moving your HTML into Mustache templates to keep your code maintainable and your site performant.
0 replies
A thoughtful contribution can make all the difference. Be the first to share one.