Moodle frankenstyle is the naming convention that gives every plugin and core subsystem one unique machine name: the plugin type prefix, an underscore, then the plugin's folder name in lower case. The quiz activity is mod_quiz, the Text block is block_html, the user upload admin tool is tool_uploaduser, and the course subsystem is core_course. Moodle uses that one string to find the plugin's code, its language strings, its database tables, its capabilities, its settings and its web service functions.
If you write or review Moodle plugins, getting the frankenstyle name right on day one saves a painful rename later, because the name is baked into table names, capability names and stored settings. This reference shows the format, the rules the code actually enforces, every place the name appears, and the exceptions that still trip people up, with examples taken from the core code base.
Checked against the Moodle 5.3.0 source (lib/components.json, public/lib/classes/component.php and public/lib/classes/param.php) and the Moodle developer documentation on frankenstyle and plugin types, October 2026. The rules below have not changed in the 4.x and 5.x branches apart from the reserved qtype_random name noted later.
The format: type prefix plus folder name
A frankenstyle component name has two parts separated by the first underscore:
- The plugin type prefix, which says what kind of plugin it is and therefore which directory it lives in. Activity modules use
mod, blocks useblock, admin tools usetool. - The plugin name, which is exactly the name of the plugin's folder, always in lower case.
Examples from Moodle 5.3, with paths relative to the public/ web root that Moodle has used since 5.1:
| Component | Type | Folder | What it is |
|---|---|---|---|
mod_forum | mod | public/mod/forum | Forum activity |
block_html | block | public/blocks/html | Text block |
tool_uploaduser | tool | public/admin/tool/uploaduser | Upload users admin tool |
auth_ldap | auth | public/auth/ldap | LDAP authentication |
qtype_calculated | qtype | public/question/type/calculated | Calculated question type |
format_topics | format | public/course/format/topics | Custom sections course format |
assignsubmission_file | assignsubmission (subplugin) | public/mod/assign/submission/file | File submissions for Assignment |
core_course | core subsystem | not a plugin | Course subsystem |
Notice that the prefix and the directory are not always the same word. Blocks live in blocks/, admin tools in admin/tool/, question types in question/type/. The mapping from prefix to directory is defined in lib/components.json, which in Moodle 5.3 lists 45 plugin types plus one deprecated type (mnetservice). For the full list with what each type does, see our guide to Moodle plugin types.
The naming rules Moodle enforces
The developer docs describe the rules, but the code is what decides whether your plugin loads. Plugin names are checked in core_component::is_valid_plugin_name():
- Most plugin types must match
/^[a-z](?:[a-z0-9_](?!__))*[a-z0-9]+$/. The name starts with a lower-case letter, contains only lower-case letters, digits and single underscores, and ends with a letter or digit. Hyphens and capitals are not allowed. - Activity modules must match
/^[a-z][a-z0-9]*$/: no underscores at all. The source comment is blunt about why: component normalisation "would break very badly otherwise". - An activity module cannot share its name with a core subsystem. A module called
courseorgradeswould be indistinguishable fromcore_courseorcore_gradeswhen written in short form. qtype_randomis reserved. The random question type was removed from core, and the validator rejects that name because backup and restore still give it special handling.
A plugin whose folder name breaks these rules is not reported with an error. The plugin types documentation says it plainly: such a plugin "will be silently ignored". If a new plugin never appears on the Plugins overview page and the upgrade screen does not offer to install it, check the folder name before anything else.
Full component names are validated separately by clean_param() with PARAM_COMPONENT. That check accepts only normalised names, rejects double underscores, and rejects any mod_ name with more than one underscore. Use it when a component name arrives from user input:
$component = clean_param($rawcomponent, PARAM_COMPONENT);
if ($component === '') {
throw new \moodle_exception('invalidcomponent', 'error');
}
Treat this snippet as illustrative: it is not run on a live site; test on staging first.
Core subsystems and how short names are resolved
Core subsystems are not plugins, but they have frankenstyle names too: core_ plus the subsystem name, for example core_course, core_user, core_grades or core_reportbuilder. Moodle 5.3 lists 80 subsystems in lib/components.json.
Older code and some APIs still accept short names, and core_component::normalize_component() turns them into a type and name pair using these rules:
| Input | Result | Why |
|---|---|---|
moodle, core or an empty string | core | The core component itself |
course (no underscore, a known subsystem) | core_course | Matches a subsystem name |
forum (no underscore, not a subsystem) | mod_forum | Anything else without an underscore is treated as an activity module |
moodle_course | core_course | The moodle type is rewritten to core |
block_html | block_html | Split on the first underscore |
That fallback is the reason activity modules cannot contain underscores and cannot reuse subsystem names. It is also why get_string('lockupdated', 'forum') still works in core JavaScript: forum normalises to mod_forum. In new code, always write the full name. core_component::normalize_componentname() returns the normalised string, and core_component::get_component_directory() returns the plugin's absolute path.
Where the frankenstyle name appears
Once you pick a name, it shows up in at least a dozen places. Examples are from core plugins in 5.3:
| Place | Pattern | Example |
|---|---|---|
version.php | $plugin->component | $plugin->component = 'block_html'; |
| Language file | lang/en/{component}.php | blocks/html/lang/en/block_html.php |
| Strings | Second argument of get_string() | get_string('pluginname', 'block_html') |
| Autoloaded classes | Namespace under classes/ | \mod_forum\local\..., \tool_uploaduser\... |
| Capabilities | Underscore becomes a slash | mod/forum:viewdiscussion, block/html:addinstance |
| Database tables | Frankenstyle name as table prefix | block_recentlyaccesseditems, tool_dataprivacy_request |
| Plugin settings | plugin column of config_plugins | get_config('tool_task', 'lastcronstart') |
| Web service functions | Function name prefix | mod_forum_get_forums_by_courses |
| Mustache templates | {component}/{template} | mod_forum/discussion_list |
| JavaScript modules | {component}/{module} | mod_forum/discussion_list from amd/src/discussion_list.js |
| Global functions and constants | Prefix, upper case for constants | local_example_do_thing(), LOCAL_EXAMPLE_LIMIT |
| CLI scripts | Component arguments | php admin/cli/cfg.php --component=tool_recyclebin, php admin/cli/uninstall_plugins.php --plugins=mod_assign |
| PHPDoc | @package | @package mod_forum |
The coding style now says non-namespaced classes named with the frankenstyle prefix (mod_forum_example) are deprecated in favour of namespaced ones (\mod_forum\example). Global functions and constants are discouraged in favour of autoloaded classes and class constants, but when you do need them they must carry the prefix.
The activity module exceptions
Activity modules are the oldest plugin type, and three legacy shortcuts survive:
- Tables drop the
mod_prefix. The forum's tables areforum,forum_postsand so on, notmod_forum_posts. - The language file drops the prefix. It is
mod/forum/lang/en/forum.php, while a block's isblock_html.php. lib.phpcallbacks use the bare module name. Moodle callsforum_add_instance()andforum_supports(). The frankenstyle docs allowmodulename_as a function prefix for modules for backwards compatibility.
Everything newer uses the full name even for modules: the \mod_forum namespace, the mod/forum:... capabilities, the mod_forum/... templates and JavaScript modules, and $plugin->component = 'mod_forum' in version.php.
Subplugins
Some plugins define their own plugin types. Assignment declares them in mod/assign/db/subplugins.json: the type assignsubmission lives in mod/assign/submission and assignfeedback in mod/assign/feedback. A subplugin's frankenstyle name uses that type as its prefix, so the file submission plugin is assignsubmission_file, with the language file lang/en/assignsubmission_file.php.
Because normalize_component() treats any unknown prefix as a possible subplugin type, a typo in a subplugin prefix does not fail loudly. Check names against the parent's subplugins.json.
Worked example: naming a new local plugin
Say you are building company-specific reports as a local plugin and choose the name acmereports. Every name follows from that choice:
| Item | Name |
|---|---|
| Component | local_acmereports |
| Folder (Moodle 5.1 and later) | public/local/acmereports |
| Language file | lang/en/local_acmereports.php |
| Namespace | \local_acmereports, classes under classes/ |
| Tables | local_acmereports, local_acmereports_runs |
| Capability | local/acmereports:view |
| Web service function | local_acmereports_get_report |
| Template and JavaScript module | local_acmereports/report |
The minimum version.php:
<?php
defined('MOODLE_INTERNAL') || die();
$plugin->component = 'local_acmereports';
$plugin->version = 2026103100;
$plugin->requires = 2026100500; // Moodle 5.3; set to the oldest release you support.
$plugin->maturity = MATURITY_ALPHA;
$plugin->release = '0.1.0';
This file is not run on a live site; test on staging first, and set requires from the root version.php of the oldest Moodle release you support (2026100500 is 5.3). Moodle uses $plugin->component during install and upgrade to confirm the code sits in the right folder, so a mismatch between this value and the directory stops the install or upgrade with a misplaced-plugin error.
Two naming choices are worth making deliberately. Avoid underscores in the plugin name even where they are allowed: the PARAM_COMPONENT docblock notes that numbers and underscores are "strongly discouraged". And if you may publish the plugin, check the Moodle Plugins directory first, because the directory requires a unique frankenstyle name and will not accept a duplicate.
Common naming mistakes and their symptoms
| Mistake | What happens | Fix |
|---|---|---|
Underscore in an activity module name (mod/my_activity) | Plugin silently ignored | Rename the folder to myactivity before first install |
| Capitals or hyphens in the folder name | Plugin silently ignored | Lower-case letters, digits and single underscores only |
$plugin->component does not match the folder | Install or upgrade stops with a location error | Make the folder and the component agree |
Language file named after the folder only (lang/en/acmereports.php for a local plugin) | Strings show as [[pluginname]] | Use the full component name; only modules use the short form |
| Tables without the prefix | Name collisions with other plugins and a schema that breaks the frankenstyle rules | Prefix every table with the component name |
Theme named after its parent (theme_boostacme) | Confusing names once the parent changes | The frankenstyle docs advise against including the parent theme name |
Renaming an installed plugin is not a find-and-replace job. Tables, stored settings in config_plugins, capabilities assigned to roles, scheduled task records and registered web service functions all carry the old name, so a rename is effectively a new plugin plus a data migration. Decide the name before the first install on any shared environment. For directory-specific rules that also affect naming, such as plugins that overlap with Moodle Workplace, see Moodle Workplace plugin development, and for the wider picture, our Moodle guide.
How we can help
We build and review Moodle plugins for organisations that need features core does not offer, from local reports to custom activity modules and admin tools, with names, schemas and capabilities planned before the first install. See our Moodle plugin development service, our wider Moodle services, or contact us with what you want to build.
Frequently asked questions
What is frankenstyle in Moodle?
Frankenstyle is Moodle's naming convention for components: the plugin type prefix, an underscore and the plugin's folder name, for example mod_forum or local_myplugin. Core subsystems use core_ plus the subsystem name, such as core_course.
Can a Moodle plugin name contain an underscore?
For most plugin types, yes, as long as there are no double underscores and the name ends with a letter or digit. Activity modules are the exception: their names may contain only lower-case letters and digits. Core still discourages underscores and numbers in any plugin name.
Why does my new Moodle plugin not appear on the upgrade page?
A common cause is an invalid folder name. Moodle silently ignores plugins whose machine name has capitals, hyphens, double underscores or, for activity modules, any underscore. Also check that $plugin->component in version.php matches the folder.
Why are forum tables not called mod_forum_posts?
Activity modules predate the frankenstyle convention, so their tables, language files and lib.php callbacks use the bare module name. Newer parts of a module, such as its namespace, capabilities and templates, use the full mod_forum name.
Free 30-minute consultation
Not sure where to start? Talk to an LMS engineer.
Tell us what platform you run and what you need. You’ll get honest, practical advice - even if the answer is that you don’t need us.
Sources & references
- Frankenstyle component names - Moodle
- Plugin types - Moodle
- version.php - Moodle
- lib/components.json (Moodle 5.3.0) - Moodle
- core_component class (Moodle 5.3.0) - Moodle
Learn more about this topic with AI
Gemini opens with the prompt copied - paste it into the chat to start.