A custom activity module (mod_*) is the right build when a workflow needs real gradebook, completion, and backup/restore integration inside a course — not something a block, local plugin, or generic content type can fake. Here's how it actually works, callback by callback.
Core Moodle activities, H5P, and LTI content types cover most course needs. But when a workflow needs its own database structure, its own grading logic, or teacher tools that don't exist anywhere else, teams often force the closest existing activity to do the job — and end up fighting its assumptions at every step instead of building the activity the workflow actually needs.
Course-embedded, gradable functionality bolted onto a block or local plugin that was never built to hold gradebook or completion hooks.
Content types pushed into multi-stage workflows or custom data models they were never designed to express.
Custom activity data that silently disappears on course backup, import, or migration because the backup classes were never built.
An activity module is the heaviest of Moodle's plugin types for a reason — it's the only one built specifically for course-embedded, gradable, backed-up content.
A Moodle activity module (frankenstyle prefix mod_, living in /mod/pluginname) is what a teacher adds directly into a course section as a new activity, exactly like Assignment, Quiz, or Forum. It gets its own course-module instance per placement, its own database tables, direct gradebook integration, completion tracking, and — critically — first-class support in Moodle's backup and restore engine. A local plugin or block can display information or run logic, but neither has course-module instances or native gradebook hooks; forcing gradable, course-embedded functionality into either means reimplementing pieces of the module API badly, by hand.
H5P and LTI content types are often the faster, correct answer — H5P for self-contained interactive content, LTI for launching an external tool with grade passback. A custom activity module earns its cost when the workflow needs a data model or teacher-facing logic those formats can't express: multi-stage submission and review flows, custom reporting tied to your own database tables, or grading rules more specific than points and scales.
Moodle's module API expects a specific set of callbacks and files — miss one and the activity either won't appear in "Add an activity" or won't behave correctly once added.
Defines the activity's settings form using Moodle's moodleform library — name, description, grading options, availability, and any custom fields your activity needs.
Holds the required callback functions Moodle core calls directly: <modname>_add_instance(), <modname>_update_instance(), <modname>_delete_instance(), plus optional hooks like <modname>_supports() and navigation callbacks.
The main student- and teacher-facing page for one instance of the activity, responsible for loading the course-module context and rendering the activity's actual content.
Same schema-definition and versioning pattern as any other plugin type, plus the activity-specific requirement that at least one instance table exists to hold per-activity data.
backup_<modname>_activity_task.class.php and restore_<modname>_activity_task.class.php, which teach Moodle's backup engine how to package and rebuild this activity's data.
Every label, setting name, and capability string the activity shows, including the required modulename and pluginname entries Moodle looks up when listing activity types.
Moodle core calls a small set of functions in lib.php directly by name, matched against the module's frankenstyle name — this is what makes the module discoverable and functional at all. <modname>_add_instance($data) runs when a teacher first adds the activity to a course, inserting the instance record and returning its ID. <modname>_update_instance($data) runs on every settings edit. <modname>_delete_instance($id) handles cleanup, including any grade items and files tied to that instance, when the activity is removed. <modname>_supports($feature) declares which core features the module participates in — FEATURE_GRADE_HAS_GRADE, FEATURE_COMPLETION_TRACKING, FEATURE_BACKUP_MOODLE2, and others — and is what tells Moodle's course page, gradebook, and backup engine whether to even look for the rest of this integration in the first place.
These four integrations are what separate a real activity module from a page that merely looks like one inside a course.
grade_update() in lib.php pushes scores into Moodle's gradebook through the Grade API, and <modname>_grade_item_update() defines how the activity's grade item is configured — points, scale, or none.
Activities declare support for completion via <modname>_supports(FEATURE_COMPLETION_TRACKING), then either let Moodle track manual/automatic completion or implement custom completion conditions the activity itself controls.
Optional <modname>_core_calendar_provide_event_action() and related callbacks let an activity's due dates or events appear correctly on the Moodle calendar and in the "This week" / timeline views.
Without correct backup/restore classes, an activity's data silently doesn't survive a course backup, a course import, or a Moodle site migration — one of the most common gaps in half-finished custom modules.
Every activity module we build uses Moodle's actual module API — proper lib.php callbacks, grade hooks, and backup/restore — not a block or local plugin stretched to look like an activity.
Grades post correctly, completion tracks correctly, and the activity survives a course backup — the three things half-finished custom modules usually get wrong.
We've built and reviewed activity modules across dozens of Moodle installs, so we know exactly where the module API's edge cases live.
Your activity module's full source lives on your Moodle installation. Nothing rented, nothing locked behind an ongoing license with us.
Straight answers to what comes up most when planning a custom activity module build.
An activity module is a mod_* plugin type that adds a new kind of course activity — something a teacher can add to a course section, that gets its own course-module instance, and that students interact with directly, like Assignment or Forum in core Moodle. It's the plugin type built specifically to support gradebook, completion, and backup/restore for course content.
A local plugin handles cross-cutting logic that isn't tied to a course, and a block is a small widget in a sidebar region — neither has course-module instances, gradebook hooks, or backup/restore support. An activity module is specifically built for content students engage with inside a course and that a teacher may need to grade.
At minimum, <modname>_add_instance(), <modname>_update_instance(), and <modname>_delete_instance() — Moodle calls these directly when a teacher adds, edits, or removes an instance of your activity from a course. Most real modules also implement <modname>_supports() to declare feature support like grading and completion.
Yes — that's done through the Grade API. <modname>_grade_item_update() defines the grade item (points, scale, or none) when an instance is created or updated, and grade_update() pushes actual scores into it as students complete the activity.
Only if you build it to — this is one of the most commonly skipped pieces. It requires backup_<modname>_activity_task.class.php and restore_<modname>_activity_task.class.php under backup/moodle2/, which teach Moodle's backup engine how to package and rebuild the activity's data during a course backup, import, or site migration.
H5P and LTI are the right call when an existing content type already covers what you need — interactive video, external tool launches, and so on. A custom activity module is the right call when you need first-class Moodle gradebook and completion integration, custom database structures specific to your workflow, or behavior H5P/LTI simply can't express, like multi-stage teacher workflows tied to course roles and capabilities.
It depends heavily on gradebook and completion complexity, but a focused custom activity module — settings form, core CRUD, grading, and backup/restore — typically runs several weeks of development plus a testing pass across roles and Moodle versions before it's ready for a live course.
The hub for our custom Moodle plugin and theme development services.
How local_* plugins work, and when they're the right build instead of a full activity module.
How we build Boost child themes specifically to survive Moodle core upgrades.
The full overview of our Moodle development services, pricing, and process.
Tell us about the workflow your course needs and we'll scope an activity module built against Moodle's real module API — grading, completion, and backup included.
Talk to a Moodle Developer